From patchwork Tue Jan 5 16:31:08 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 563188 X-Patchwork-Delegate: agust@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by ozlabs.org (Postfix) with ESMTP id CC793140B98 for ; Wed, 6 Jan 2016 03:34:11 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.b=UwRiFovN; dkim-atps=neutral Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 8D5FA4B8BB; Tue, 5 Jan 2016 17:32:54 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id F1I2CCyWsE0Q; Tue, 5 Jan 2016 17:32:54 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id C7FBF4B8B8; Tue, 5 Jan 2016 17:32:16 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 262914B855 for ; Tue, 5 Jan 2016 17:31:52 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id QoBdCVKEAvwM for ; Tue, 5 Jan 2016 17:31:52 +0100 (CET) X-policyd-weight: NOT_IN_SBL_XBL_SPAMHAUS=-1.5 NOT_IN_SPAMCOP=-1.5 NOT_IN_BL_NJABL=-1.5 (only DNSBL check requested) Received: from mail-ob0-f177.google.com (mail-ob0-f177.google.com [209.85.214.177]) by theia.denx.de (Postfix) with ESMTPS id 07DFA4B803 for ; Tue, 5 Jan 2016 17:31:40 +0100 (CET) Received: by mail-ob0-f177.google.com with SMTP id bx1so251285903obb.0 for ; Tue, 05 Jan 2016 08:31:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=FUbXpaTlGOJbUJWa7IRdWFzSfggu7IBEmXUcbiaZEdg=; b=UwRiFovNBN8pW87MKQW9rTWOARtcSxKnf+KWZbUsFsqShMgbP6TWbPYtXyWiWqi1ln nCGJIB0Y/fcile+9AdDQnkpflXKMi+LSYx1aQmJxZlj3k59BqS8T9Lsfr18lT92DLSCA xLhQnKLFq60RMXudzSWqpyl9oQagqxx4DZ/spIsrGWBu5C1Ubhv5+EA0TRzSMXGMcyNb uO31jA9nzW2ravoyz6s+tQrnpXgB6974+uNX1+r7p5dG/beDi6ETea72X6yT34L8zGCf ZmmnlkDkctSF+bOQTI8WfwuC7zquHIpDJf5FYPli1TAw1asNVAm6Mloyi+xPOVTPcdsJ mLQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=FUbXpaTlGOJbUJWa7IRdWFzSfggu7IBEmXUcbiaZEdg=; b=EzkCF5sxuMhjIv5o499iMvg83s/+KAtPlUnpSmDmfpbzgRiIeaAA90RmGz9AF0cr0z u3NFRITRJfQ1CrL/yvUJjijHvpZNKKYXbOkKZQTfkPHOXiuemI89Hr+rG6LtqsZEK6z/ y+L/Bj6Q4c8/5RLFYV/ylUxUfqDs5fbT7aM8v+DcdxWsIs06xJQH0pSXpbHUXD/en8ek ZEQH1DrgHFbxGxsEt1HtwuecuDsOIZOUcjZdt9Rn2Mi+XMFimqs5UWv4RD3hEhjc0/HP TZM81fnl+C8tqUT3qe1+KaGNlAkfZF2JUDcNsftU1bg7sns/2pfJQ9W3xn9BLDmoNvsG nUuQ== X-Gm-Message-State: ALoCoQkeuQ1Qi9HxmRHbYVlagOt2jT9WkeuLM7eQ9X3GUJdH35ST+wUYKSmxF1BzbSy5oj1iN5Oxv8IxAWqrwqDAebcnzsyFTQ== X-Received: by 10.60.143.98 with SMTP id sd2mr68020367oeb.45.1452011498861; Tue, 05 Jan 2016 08:31:38 -0800 (PST) Received: from kaki.bld.corp.google.com ([172.29.216.32]) by smtp.gmail.com with ESMTPSA id y1sm34701060oes.5.2016.01.05.08.31.34 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 05 Jan 2016 08:31:35 -0800 (PST) Received: by kaki.bld.corp.google.com (Postfix, from userid 121222) id 42CB42224AA; Tue, 5 Jan 2016 09:31:31 -0700 (MST) From: Simon Glass To: U-Boot Mailing List Date: Tue, 5 Jan 2016 09:31:08 -0700 Message-Id: <1452011474-15207-13-git-send-email-sjg@chromium.org> X-Mailer: git-send-email 2.6.0.rc2.230.g3dd15c0 In-Reply-To: <1452011474-15207-1-git-send-email-sjg@chromium.org> References: <1452011474-15207-1-git-send-email-sjg@chromium.org> Cc: Joe Hershberger Subject: [U-Boot] [PATCH 12/18] dm: video: Implement the bmp command for driver model X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.15 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" This command can use the bitmap display code in the uclass. This is similar to the code in lcd.c and cfb_console.c. These other copies will go away when all boards are converted to use driver model for video. Signed-off-by: Simon Glass Acked-by: Anatolij Gustschin --- common/cmd_bmp.c | 22 ++- drivers/video/Makefile | 1 + drivers/video/video_bmp.c | 353 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 drivers/video/video_bmp.c diff --git a/common/cmd_bmp.c b/common/cmd_bmp.c index f04b7d4..fd5b7db 100644 --- a/common/cmd_bmp.c +++ b/common/cmd_bmp.c @@ -10,7 +10,9 @@ */ #include +#include #include +#include #include #include #include @@ -225,6 +227,9 @@ static int bmp_info(ulong addr) */ int bmp_display(ulong addr, int x, int y) { +#ifdef CONFIG_DM_VIDEO + struct udevice *dev; +#endif int ret; struct bmp_image *bmp = map_sysmem(addr, 0); void *bmp_alloc_addr = NULL; @@ -240,7 +245,22 @@ int bmp_display(ulong addr, int x, int y) } addr = map_to_sysmem(bmp); -#if defined(CONFIG_LCD) +#ifdef CONFIG_DM_VIDEO + ret = uclass_first_device(UCLASS_VIDEO, &dev); + if (!ret) { + if (!dev) + ret = -ENODEV; + if (!ret) { + bool align = false; + +# ifdef CONFIG_SPLASH_SCREEN_ALIGN + align = true; +# endif /* CONFIG_SPLASH_SCREEN_ALIGN */ + ret = video_bmp_display(dev, addr, x, y, align); + } + } + return ret ? CMD_RET_FAILURE : 0; +#elif defined(CONFIG_LCD) ret = lcd_display_bitmap(addr, x, y); #elif defined(CONFIG_VIDEO) ret = video_display_bitmap(addr, x, y); diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 8f26d1d..ee04629 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -8,6 +8,7 @@ ifdef CONFIG_DM obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o console_normal.o +obj-$(CONFIG_DM_VIDEO) += video_bmp.o obj-$(CONFIG_VIDEO_ROTATION) += console_rotate.o endif diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c new file mode 100644 index 0000000..c9075d6 --- /dev/null +++ b/drivers/video/video_bmp.c @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_VIDEO_BMP_RLE8 +#define BMP_RLE8_ESCAPE 0 +#define BMP_RLE8_EOL 0 +#define BMP_RLE8_EOBMP 1 +#define BMP_RLE8_DELTA 2 + +static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap, + int cnt) +{ + while (cnt > 0) { + *(*fbp)++ = cmap[*bmap++]; + cnt--; + } +} + +static void draw_encoded_bitmap(ushort **fbp, ushort col, int cnt) +{ + ushort *fb = *fbp; + + while (cnt > 0) { + *fb++ = col; + cnt--; + } + *fbp = fb; +} + +static void video_display_rle8_bitmap(struct udevice *dev, + struct bmp_image *bmp, ushort *cmap, + uchar *fb, int x_off, int y_off) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + uchar *bmap; + ulong width, height; + ulong cnt, runlen; + int x, y; + int decode = 1; + + debug("%s\n", __func__); + width = get_unaligned_le32(&bmp->header.width); + height = get_unaligned_le32(&bmp->header.height); + bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); + + x = 0; + y = height - 1; + + while (decode) { + if (bmap[0] == BMP_RLE8_ESCAPE) { + switch (bmap[1]) { + case BMP_RLE8_EOL: + /* end of line */ + bmap += 2; + x = 0; + y--; + /* 16bpix, 2-byte per pixel, width should *2 */ + fb -= (width * 2 + priv->line_length); + break; + case BMP_RLE8_EOBMP: + /* end of bitmap */ + decode = 0; + break; + case BMP_RLE8_DELTA: + /* delta run */ + x += bmap[2]; + y -= bmap[3]; + /* 16bpix, 2-byte per pixel, x should *2 */ + fb = (uchar *)(priv->fb + (y + y_off - 1) + * priv->line_length + (x + x_off) * 2); + bmap += 4; + break; + default: + /* unencoded run */ + runlen = bmap[1]; + bmap += 2; + if (y < height) { + if (x < width) { + if (x + runlen > width) + cnt = width - x; + else + cnt = runlen; + draw_unencoded_bitmap( + (ushort **)&fb, + bmap, cmap, cnt); + } + x += runlen; + } + bmap += runlen; + if (runlen & 1) + bmap++; + } + } else { + /* encoded run */ + if (y < height) { + runlen = bmap[0]; + if (x < width) { + /* aggregate the same code */ + while (bmap[0] == 0xff && + bmap[2] != BMP_RLE8_ESCAPE && + bmap[1] == bmap[3]) { + runlen += bmap[2]; + bmap += 2; + } + if (x + runlen > width) + cnt = width - x; + else + cnt = runlen; + draw_encoded_bitmap((ushort **)&fb, + cmap[bmap[1]], cnt); + } + x += runlen; + } + bmap += 2; + } + } +} +#endif + +__weak void fb_put_byte(uchar **fb, uchar **from) +{ + *(*fb)++ = *(*from)++; +} + +#if defined(CONFIG_BMP_16BPP) +__weak void fb_put_word(uchar **fb, uchar **from) +{ + *(*fb)++ = *(*from)++; + *(*fb)++ = *(*from)++; +} +#endif /* CONFIG_BMP_16BPP */ + +#define BMP_ALIGN_CENTER 0x7fff + +/** + * video_splash_align_axis() - Align a single coordinate + * + *- if a coordinate is 0x7fff then the image will be centred in + * that direction + *- if a coordinate is -ve then it will be offset to the + * left/top of the centre by that many pixels + *- if a coordinate is positive it will be used unchnaged. + * + * @axis: Input and output coordinate + * @panel_size: Size of panel in pixels for that axis + * @picture_size: Size of bitmap in pixels for that axis + */ +static void video_splash_align_axis(int *axis, unsigned long panel_size, + unsigned long picture_size) +{ + unsigned long panel_picture_delta = panel_size - picture_size; + unsigned long axis_alignment; + + if (*axis == BMP_ALIGN_CENTER) + axis_alignment = panel_picture_delta / 2; + else if (*axis < 0) + axis_alignment = panel_picture_delta + *axis + 1; + else + return; + + *axis = max(0, (int)axis_alignment); +} + +static void video_set_cmap(struct udevice *dev, + struct bmp_color_table_entry *cte, unsigned colours) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + int i; + ushort *cmap = priv->cmap; + + debug("%s: colours=%d\n", __func__, colours); + for (i = 0; i < colours; ++i) { + *cmap = ((cte->red << 8) & 0xf800) | + ((cte->green << 3) & 0x07e0) | + ((cte->blue >> 3) & 0x001f); + cmap++; + cte++; + } +} + +int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, + bool align) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + ushort *cmap_base = NULL; + ushort i, j; + uchar *fb; + struct bmp_image *bmp = map_sysmem(bmp_image, 0); + uchar *bmap; + ushort padded_width; + unsigned long width, height, byte_width; + unsigned long pwidth = priv->xsize; + unsigned colours, bpix, bmp_bpix; + struct bmp_color_table_entry *palette; + int hdr_size; + + if (!bmp || !(bmp->header.signature[0] == 'B' && + bmp->header.signature[1] == 'M')) { + printf("Error: no valid bmp image at %lx\n", bmp_image); + + return -EINVAL; + } + + width = get_unaligned_le32(&bmp->header.width); + height = get_unaligned_le32(&bmp->header.height); + bmp_bpix = get_unaligned_le16(&bmp->header.bit_count); + hdr_size = get_unaligned_le16(&bmp->header.size); + debug("hdr_size=%d, bmp_bpix=%d\n", hdr_size, bmp_bpix); + palette = (void *)bmp + 14 + hdr_size; + + colours = 1 << bmp_bpix; + + bpix = VNBITS(priv->bpix); + + if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) { + printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", + bpix, bmp_bpix); + + return -EINVAL; + } + + /* + * We support displaying 8bpp BMPs on 16bpp LCDs + * and displaying 24bpp BMPs on 32bpp LCDs + * */ + if (bpix != bmp_bpix && + !(bmp_bpix == 8 && bpix == 16) && + !(bmp_bpix == 24 && bpix == 32)) { + printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", + bpix, get_unaligned_le16(&bmp->header.bit_count)); + return -EPERM; + } + + debug("Display-bmp: %d x %d with %d colours, display %d\n", + (int)width, (int)height, (int)colours, 1 << bpix); + + if (bmp_bpix == 8) + video_set_cmap(dev, palette, colours); + + padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width); + + if (align) { + video_splash_align_axis(&x, priv->xsize, width); + video_splash_align_axis(&y, priv->ysize, height); + } + + if ((x + width) > pwidth) + width = pwidth - x; + if ((y + height) > priv->ysize) + height = priv->ysize - y; + + bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset); + fb = (uchar *)(priv->fb + + (y + height - 1) * priv->line_length + x * bpix / 8); + + switch (bmp_bpix) { + case 1: + case 8: { + cmap_base = priv->cmap; +#ifdef CONFIG_VIDEO_BMP_RLE8 + u32 compression = get_unaligned_le32(&bmp->header.compression); + debug("compressed %d %d\n", compression, BMP_BI_RLE8); + if (compression == BMP_BI_RLE8) { + if (bpix != 16) { + /* TODO implement render code for bpix != 16 */ + printf("Error: only support 16 bpix"); + return -EPROTONOSUPPORT; + } + video_display_rle8_bitmap(dev, bmp, cmap_base, fb, x, + y); + break; + } +#endif + + if (bpix != 16) + byte_width = width; + else + byte_width = width * 2; + + for (i = 0; i < height; ++i) { + WATCHDOG_RESET(); + for (j = 0; j < width; j++) { + if (bpix != 16) { + fb_put_byte(&fb, &bmap); + } else { + *(uint16_t *)fb = cmap_base[*bmap]; + bmap++; + fb += sizeof(uint16_t) / sizeof(*fb); + } + } + bmap += (padded_width - width); + fb -= byte_width + priv->line_length; + } + break; + } +#if defined(CONFIG_BMP_16BPP) + case 16: + for (i = 0; i < height; ++i) { + WATCHDOG_RESET(); + for (j = 0; j < width; j++) + fb_put_word(&fb, &bmap); + + bmap += (padded_width - width) * 2; + fb -= width * 2 + lcd_line_length; + } + break; +#endif /* CONFIG_BMP_16BPP */ +#if defined(CONFIG_BMP_24BMP) + case 24: + for (i = 0; i < height; ++i) { + for (j = 0; j < width; j++) { + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = 0; + } + fb -= lcd_line_length + width * (bpix / 8); + } + break; +#endif /* CONFIG_BMP_24BMP */ +#if defined(CONFIG_BMP_32BPP) + case 32: + for (i = 0; i < height; ++i) { + for (j = 0; j < width; j++) { + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + *(fb++) = *(bmap++); + } + fb -= lcd_line_length + width * (bpix / 8); + } + break; +#endif /* CONFIG_BMP_32BPP */ + default: + break; + }; + + video_sync(dev); + + return 0; +} +