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