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 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 /** 145 * video_splash_align_axis() - Align a single coordinate 146 * 147 *- if a coordinate is 0x7fff then the image will be centred in 148 * that direction 149 *- if a coordinate is -ve then it will be offset to the 150 * left/top of the centre by that many pixels 151 *- if a coordinate is positive it will be used unchnaged. 152 * 153 * @axis: Input and output coordinate 154 * @panel_size: Size of panel in pixels for that axis 155 * @picture_size: Size of bitmap in pixels for that axis 156 */ 157 static void video_splash_align_axis(int *axis, unsigned long panel_size, 158 unsigned long picture_size) 159 { 160 unsigned long panel_picture_delta = panel_size - picture_size; 161 unsigned long axis_alignment; 162 163 if (*axis == BMP_ALIGN_CENTER) 164 axis_alignment = panel_picture_delta / 2; 165 else if (*axis < 0) 166 axis_alignment = panel_picture_delta + *axis + 1; 167 else 168 return; 169 170 *axis = max(0, (int)axis_alignment); 171 } 172 173 static void video_set_cmap(struct udevice *dev, 174 struct bmp_color_table_entry *cte, unsigned colours) 175 { 176 struct video_priv *priv = dev_get_uclass_priv(dev); 177 int i; 178 ushort *cmap = priv->cmap; 179 180 debug("%s: colours=%d\n", __func__, colours); 181 for (i = 0; i < colours; ++i) { 182 *cmap = ((cte->red << 8) & 0xf800) | 183 ((cte->green << 3) & 0x07e0) | 184 ((cte->blue >> 3) & 0x001f); 185 cmap++; 186 cte++; 187 } 188 } 189 190 int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y, 191 bool align) 192 { 193 struct video_priv *priv = dev_get_uclass_priv(dev); 194 ushort *cmap_base = NULL; 195 int i, j; 196 uchar *fb; 197 struct bmp_image *bmp = map_sysmem(bmp_image, 0); 198 uchar *bmap; 199 ushort padded_width; 200 unsigned long width, height, byte_width; 201 unsigned long pwidth = priv->xsize; 202 unsigned colours, bpix, bmp_bpix; 203 struct bmp_color_table_entry *palette; 204 int hdr_size; 205 206 if (!bmp || !(bmp->header.signature[0] == 'B' && 207 bmp->header.signature[1] == 'M')) { 208 printf("Error: no valid bmp image at %lx\n", bmp_image); 209 210 return -EINVAL; 211 } 212 213 width = get_unaligned_le32(&bmp->header.width); 214 height = get_unaligned_le32(&bmp->header.height); 215 bmp_bpix = get_unaligned_le16(&bmp->header.bit_count); 216 hdr_size = get_unaligned_le16(&bmp->header.size); 217 debug("hdr_size=%d, bmp_bpix=%d\n", hdr_size, bmp_bpix); 218 palette = (void *)bmp + 14 + hdr_size; 219 220 colours = 1 << bmp_bpix; 221 222 bpix = VNBITS(priv->bpix); 223 224 if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) { 225 printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n", 226 bpix, bmp_bpix); 227 228 return -EINVAL; 229 } 230 231 /* 232 * We support displaying 8bpp and 24bpp BMPs on 16bpp LCDs 233 * and displaying 24bpp BMPs on 32bpp LCDs 234 */ 235 if (bpix != bmp_bpix && 236 !(bmp_bpix == 8 && bpix == 16) && 237 !(bmp_bpix == 24 && 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 if (bpix == 16) { 323 /* 16bit 555RGB format */ 324 *(u16 *)fb = ((bmap[2] >> 3) << 10) | 325 ((bmap[1] >> 3) << 5) | 326 (bmap[0] >> 3); 327 bmap += 3; 328 fb += 2; 329 } else { 330 *(fb++) = *(bmap++); 331 *(fb++) = *(bmap++); 332 *(fb++) = *(bmap++); 333 *(fb++) = 0; 334 } 335 } 336 fb -= priv->line_length + width * (bpix / 8); 337 bmap += (padded_width - width) * 3; 338 } 339 break; 340 #endif /* CONFIG_BMP_24BPP */ 341 #if defined(CONFIG_BMP_32BPP) 342 case 32: 343 for (i = 0; i < height; ++i) { 344 for (j = 0; j < width; j++) { 345 *(fb++) = *(bmap++); 346 *(fb++) = *(bmap++); 347 *(fb++) = *(bmap++); 348 *(fb++) = *(bmap++); 349 } 350 fb -= priv->line_length + width * (bpix / 8); 351 } 352 break; 353 #endif /* CONFIG_BMP_32BPP */ 354 default: 355 break; 356 }; 357 358 video_sync(dev, false); 359 360 return 0; 361 } 362 363