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