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