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