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