Home | History | Annotate | Download | only in video
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Copyright (c) 2015 Google, Inc
      4  */
      5 
      6 #include <common.h>
      7 #include <bmp_layout.h>
      8 #include <dm.h>
      9 #include <mapmem.h>
     10 #include <video.h>
     11 #include <watchdog.h>
     12 #include <asm/unaligned.h>
     13 
     14 #ifdef CONFIG_VIDEO_BMP_RLE8
     15 #define BMP_RLE8_ESCAPE		0
     16 #define BMP_RLE8_EOL		0
     17 #define BMP_RLE8_EOBMP		1
     18 #define BMP_RLE8_DELTA		2
     19 
     20 static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap,
     21 				  int cnt)
     22 {
     23 	while (cnt > 0) {
     24 		*(*fbp)++ = cmap[*bmap++];
     25 		cnt--;
     26 	}
     27 }
     28 
     29 static void draw_encoded_bitmap(ushort **fbp, ushort col, int cnt)
     30 {
     31 	ushort *fb = *fbp;
     32 
     33 	while (cnt > 0) {
     34 		*fb++ = col;
     35 		cnt--;
     36 	}
     37 	*fbp = fb;
     38 }
     39 
     40 static void video_display_rle8_bitmap(struct udevice *dev,
     41 				      struct bmp_image *bmp, ushort *cmap,
     42 				      uchar *fb, int x_off, int y_off)
     43 {
     44 	struct video_priv *priv = dev_get_uclass_priv(dev);
     45 	uchar *bmap;
     46 	ulong width, height;
     47 	ulong cnt, runlen;
     48 	int x, y;
     49 	int decode = 1;
     50 
     51 	debug("%s\n", __func__);
     52 	width = get_unaligned_le32(&bmp->header.width);
     53 	height = get_unaligned_le32(&bmp->header.height);
     54 	bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
     55 
     56 	x = 0;
     57 	y = height - 1;
     58 
     59 	while (decode) {
     60 		if (bmap[0] == BMP_RLE8_ESCAPE) {
     61 			switch (bmap[1]) {
     62 			case BMP_RLE8_EOL:
     63 				/* end of line */
     64 				bmap += 2;
     65 				x = 0;
     66 				y--;
     67 				/* 16bpix, 2-byte per pixel, width should *2 */
     68 				fb -= (width * 2 + priv->line_length);
     69 				break;
     70 			case BMP_RLE8_EOBMP:
     71 				/* end of bitmap */
     72 				decode = 0;
     73 				break;
     74 			case BMP_RLE8_DELTA:
     75 				/* delta run */
     76 				x += bmap[2];
     77 				y -= bmap[3];
     78 				/* 16bpix, 2-byte per pixel, x should *2 */
     79 				fb = (uchar *)(priv->fb + (y + y_off - 1)
     80 					* priv->line_length + (x + x_off) * 2);
     81 				bmap += 4;
     82 				break;
     83 			default:
     84 				/* unencoded run */
     85 				runlen = bmap[1];
     86 				bmap += 2;
     87 				if (y < height) {
     88 					if (x < width) {
     89 						if (x + runlen > width)
     90 							cnt = width - x;
     91 						else
     92 							cnt = runlen;
     93 						draw_unencoded_bitmap(
     94 							(ushort **)&fb,
     95 							bmap, cmap, cnt);
     96 					}
     97 					x += runlen;
     98 				}
     99 				bmap += runlen;
    100 				if (runlen & 1)
    101 					bmap++;
    102 			}
    103 		} else {
    104 			/* encoded run */
    105 			if (y < height) {
    106 				runlen = bmap[0];
    107 				if (x < width) {
    108 					/* aggregate the same code */
    109 					while (bmap[0] == 0xff &&
    110 					       bmap[2] != BMP_RLE8_ESCAPE &&
    111 					       bmap[1] == bmap[3]) {
    112 						runlen += bmap[2];
    113 						bmap += 2;
    114 					}
    115 					if (x + runlen > width)
    116 						cnt = width - x;
    117 					else
    118 						cnt = runlen;
    119 					draw_encoded_bitmap((ushort **)&fb,
    120 						cmap[bmap[1]], cnt);
    121 				}
    122 				x += runlen;
    123 			}
    124 			bmap += 2;
    125 		}
    126 	}
    127 }
    128 #endif
    129 
    130 __weak void fb_put_byte(uchar **fb, uchar **from)
    131 {
    132 	*(*fb)++ = *(*from)++;
    133 }
    134 
    135 #if defined(CONFIG_BMP_16BPP)
    136 __weak void fb_put_word(uchar **fb, uchar **from)
    137 {
    138 	*(*fb)++ = *(*from)++;
    139 	*(*fb)++ = *(*from)++;
    140 }
    141 #endif /* CONFIG_BMP_16BPP */
    142 
    143 #define BMP_ALIGN_CENTER	0x7fff
    144 
    145 /**
    146  * video_splash_align_axis() - Align a single coordinate
    147  *
    148  *- if a coordinate is 0x7fff then the image will be centred in
    149  *  that direction
    150  *- if a coordinate is -ve then it will be offset to the
    151  *  left/top of the centre by that many pixels
    152  *- if a coordinate is positive it will be used unchnaged.
    153  *
    154  * @axis:	Input and output coordinate
    155  * @panel_size:	Size of panel in pixels for that axis
    156  * @picture_size:	Size of bitmap in pixels for that axis
    157  */
    158 static void video_splash_align_axis(int *axis, unsigned long panel_size,
    159 				    unsigned long picture_size)
    160 {
    161 	unsigned long panel_picture_delta = panel_size - picture_size;
    162 	unsigned long axis_alignment;
    163 
    164 	if (*axis == BMP_ALIGN_CENTER)
    165 		axis_alignment = panel_picture_delta / 2;
    166 	else if (*axis < 0)
    167 		axis_alignment = panel_picture_delta + *axis + 1;
    168 	else
    169 		return;
    170 
    171 	*axis = max(0, (int)axis_alignment);
    172 }
    173 
    174 static void video_set_cmap(struct udevice *dev,
    175 			   struct bmp_color_table_entry *cte, unsigned colours)
    176 {
    177 	struct video_priv *priv = dev_get_uclass_priv(dev);
    178 	int i;
    179 	ushort *cmap = priv->cmap;
    180 
    181 	debug("%s: colours=%d\n", __func__, colours);
    182 	for (i = 0; i < colours; ++i) {
    183 		*cmap = ((cte->red   << 8) & 0xf800) |
    184 			((cte->green << 3) & 0x07e0) |
    185 			((cte->blue  >> 3) & 0x001f);
    186 		cmap++;
    187 		cte++;
    188 	}
    189 }
    190 
    191 int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
    192 		      bool align)
    193 {
    194 	struct video_priv *priv = dev_get_uclass_priv(dev);
    195 	ushort *cmap_base = NULL;
    196 	int i, j;
    197 	uchar *fb;
    198 	struct bmp_image *bmp = map_sysmem(bmp_image, 0);
    199 	uchar *bmap;
    200 	ushort padded_width;
    201 	unsigned long width, height, byte_width;
    202 	unsigned long pwidth = priv->xsize;
    203 	unsigned colours, bpix, bmp_bpix;
    204 	struct bmp_color_table_entry *palette;
    205 	int hdr_size;
    206 
    207 	if (!bmp || !(bmp->header.signature[0] == 'B' &&
    208 	    bmp->header.signature[1] == 'M')) {
    209 		printf("Error: no valid bmp image at %lx\n", bmp_image);
    210 
    211 		return -EINVAL;
    212 	}
    213 
    214 	width = get_unaligned_le32(&bmp->header.width);
    215 	height = get_unaligned_le32(&bmp->header.height);
    216 	bmp_bpix = get_unaligned_le16(&bmp->header.bit_count);
    217 	hdr_size = get_unaligned_le16(&bmp->header.size);
    218 	debug("hdr_size=%d, bmp_bpix=%d\n", hdr_size, bmp_bpix);
    219 	palette = (void *)bmp + 14 + hdr_size;
    220 
    221 	colours = 1 << bmp_bpix;
    222 
    223 	bpix = VNBITS(priv->bpix);
    224 
    225 	if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) {
    226 		printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
    227 		       bpix, bmp_bpix);
    228 
    229 		return -EINVAL;
    230 	}
    231 
    232 	/*
    233 	 * We support displaying 8bpp BMPs on 16bpp LCDs
    234 	 * and displaying 24bpp BMPs on 32bpp LCDs
    235 	 * */
    236 	if (bpix != bmp_bpix &&
    237 	    !(bmp_bpix == 8 && bpix == 16) &&
    238 	    !(bmp_bpix == 24 && bpix == 32)) {
    239 		printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
    240 		       bpix, get_unaligned_le16(&bmp->header.bit_count));
    241 		return -EPERM;
    242 	}
    243 
    244 	debug("Display-bmp: %d x %d  with %d colours, display %d\n",
    245 	      (int)width, (int)height, (int)colours, 1 << bpix);
    246 
    247 	if (bmp_bpix == 8)
    248 		video_set_cmap(dev, palette, colours);
    249 
    250 	padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width);
    251 
    252 	if (align) {
    253 		video_splash_align_axis(&x, priv->xsize, width);
    254 		video_splash_align_axis(&y, priv->ysize, height);
    255 	}
    256 
    257 	if ((x + width) > pwidth)
    258 		width = pwidth - x;
    259 	if ((y + height) > priv->ysize)
    260 		height = priv->ysize - y;
    261 
    262 	bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
    263 	fb = (uchar *)(priv->fb +
    264 		(y + height - 1) * priv->line_length + x * bpix / 8);
    265 
    266 	switch (bmp_bpix) {
    267 	case 1:
    268 	case 8: {
    269 		cmap_base = priv->cmap;
    270 #ifdef CONFIG_VIDEO_BMP_RLE8
    271 		u32 compression = get_unaligned_le32(&bmp->header.compression);
    272 		debug("compressed %d %d\n", compression, BMP_BI_RLE8);
    273 		if (compression == BMP_BI_RLE8) {
    274 			if (bpix != 16) {
    275 				/* TODO implement render code for bpix != 16 */
    276 				printf("Error: only support 16 bpix");
    277 				return -EPROTONOSUPPORT;
    278 			}
    279 			video_display_rle8_bitmap(dev, bmp, cmap_base, fb, x,
    280 						  y);
    281 			break;
    282 		}
    283 #endif
    284 
    285 		if (bpix != 16)
    286 			byte_width = width;
    287 		else
    288 			byte_width = width * 2;
    289 
    290 		for (i = 0; i < height; ++i) {
    291 			WATCHDOG_RESET();
    292 			for (j = 0; j < width; j++) {
    293 				if (bpix != 16) {
    294 					fb_put_byte(&fb, &bmap);
    295 				} else {
    296 					*(uint16_t *)fb = cmap_base[*bmap];
    297 					bmap++;
    298 					fb += sizeof(uint16_t) / sizeof(*fb);
    299 				}
    300 			}
    301 			bmap += (padded_width - width);
    302 			fb -= byte_width + priv->line_length;
    303 		}
    304 		break;
    305 	}
    306 #if defined(CONFIG_BMP_16BPP)
    307 	case 16:
    308 		for (i = 0; i < height; ++i) {
    309 			WATCHDOG_RESET();
    310 			for (j = 0; j < width; j++)
    311 				fb_put_word(&fb, &bmap);
    312 
    313 			bmap += (padded_width - width) * 2;
    314 			fb -= width * 2 + priv->line_length;
    315 		}
    316 		break;
    317 #endif /* CONFIG_BMP_16BPP */
    318 #if defined(CONFIG_BMP_24BPP)
    319 	case 24:
    320 		for (i = 0; i < height; ++i) {
    321 			for (j = 0; j < width; j++) {
    322 				*(fb++) = *(bmap++);
    323 				*(fb++) = *(bmap++);
    324 				*(fb++) = *(bmap++);
    325 				*(fb++) = 0;
    326 			}
    327 			fb -= priv->line_length + width * (bpix / 8);
    328 		}
    329 		break;
    330 #endif /* CONFIG_BMP_24BPP */
    331 #if defined(CONFIG_BMP_32BPP)
    332 	case 32:
    333 		for (i = 0; i < height; ++i) {
    334 			for (j = 0; j < width; j++) {
    335 				*(fb++) = *(bmap++);
    336 				*(fb++) = *(bmap++);
    337 				*(fb++) = *(bmap++);
    338 				*(fb++) = *(bmap++);
    339 			}
    340 			fb -= priv->line_length + width * (bpix / 8);
    341 		}
    342 		break;
    343 #endif /* CONFIG_BMP_32BPP */
    344 	default:
    345 		break;
    346 	};
    347 
    348 	video_sync(dev);
    349 
    350 	return 0;
    351 }
    352 
    353