uboot/drivers/video/video_bmp.c
<<
>>
Prefs
   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 <video.h>
  11#include <watchdog.h>
  12#include <asm/unaligned.h>
  13
  14#ifdef CONFIG_VIDEO_BMP_RLE8
  15#define BMP_RLE8_ESCAPE         0
  16#define BMP_RLE8_EOL            0
  17#define BMP_RLE8_EOBMP          1
  18#define BMP_RLE8_DELTA          2
  19
  20static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap,
  21                                  int cnt)
  22{
  23        while (cnt > 0) {
  24                *(*fbp)++ = cmap[*bmap++];
  25                cnt--;
  26        }
  27}
  28
  29static void draw_encoded_bitmap(ushort **fbp, ushort col, int cnt)
  30{
  31        ushort *fb = *fbp;
  32
  33        while (cnt > 0) {
  34                *fb++ = col;
  35                cnt--;
  36        }
  37        *fbp = fb;
  38}
  39
  40static void video_display_rle8_bitmap(struct udevice *dev,
  41                                      struct bmp_image *bmp, ushort *cmap,
  42                                      uchar *fb, int x_off, int y_off)
  43{
  44        struct video_priv *priv = dev_get_uclass_priv(dev);
  45        uchar *bmap;
  46        ulong width, height;
  47        ulong cnt, runlen;
  48        int x, y;
  49        int decode = 1;
  50
  51        debug("%s\n", __func__);
  52        width = get_unaligned_le32(&bmp->header.width);
  53        height = get_unaligned_le32(&bmp->header.height);
  54        bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
  55
  56        x = 0;
  57        y = height - 1;
  58
  59        while (decode) {
  60                if (bmap[0] == BMP_RLE8_ESCAPE) {
  61                        switch (bmap[1]) {
  62                        case BMP_RLE8_EOL:
  63                                /* end of line */
  64                                bmap += 2;
  65                                x = 0;
  66                                y--;
  67                                /* 16bpix, 2-byte per pixel, width should *2 */
  68                                fb -= (width * 2 + priv->line_length);
  69                                break;
  70                        case BMP_RLE8_EOBMP:
  71                                /* end of bitmap */
  72                                decode = 0;
  73                                break;
  74                        case BMP_RLE8_DELTA:
  75                                /* delta run */
  76                                x += bmap[2];
  77                                y -= bmap[3];
  78                                /* 16bpix, 2-byte per pixel, x should *2 */
  79                                fb = (uchar *)(priv->fb + (y + y_off - 1)
  80                                        * priv->line_length + (x + x_off) * 2);
  81                                bmap += 4;
  82                                break;
  83                        default:
  84                                /* unencoded run */
  85                                runlen = bmap[1];
  86                                bmap += 2;
  87                                if (y < height) {
  88                                        if (x < width) {
  89                                                if (x + runlen > width)
  90                                                        cnt = width - x;
  91                                                else
  92                                                        cnt = runlen;
  93                                                draw_unencoded_bitmap(
  94                                                        (ushort **)&fb,
  95                                                        bmap, cmap, cnt);
  96                                        }
  97                                        x += runlen;
  98                                }
  99                                bmap += runlen;
 100                                if (runlen & 1)
 101                                        bmap++;
 102                        }
 103                } else {
 104                        /* encoded run */
 105                        if (y < height) {
 106                                runlen = bmap[0];
 107                                if (x < width) {
 108                                        /* aggregate the same code */
 109                                        while (bmap[0] == 0xff &&
 110                                               bmap[2] != BMP_RLE8_ESCAPE &&
 111                                               bmap[1] == bmap[3]) {
 112                                                runlen += bmap[2];
 113                                                bmap += 2;
 114                                        }
 115                                        if (x + runlen > width)
 116                                                cnt = width - x;
 117                                        else
 118                                                cnt = runlen;
 119                                        draw_encoded_bitmap((ushort **)&fb,
 120                                                cmap[bmap[1]], cnt);
 121                                }
 122                                x += runlen;
 123                        }
 124                        bmap += 2;
 125                }
 126        }
 127}
 128#endif
 129
 130__weak void fb_put_byte(uchar **fb, uchar **from)
 131{
 132        *(*fb)++ = *(*from)++;
 133}
 134
 135#if defined(CONFIG_BMP_16BPP)
 136__weak void fb_put_word(uchar **fb, uchar **from)
 137{
 138        *(*fb)++ = *(*from)++;
 139        *(*fb)++ = *(*from)++;
 140}
 141#endif /* CONFIG_BMP_16BPP */
 142
 143#define BMP_ALIGN_CENTER        0x7fff
 144
 145/**
 146 * video_splash_align_axis() - Align a single coordinate
 147 *
 148 *- if a coordinate is 0x7fff then the image will be centred in
 149 *  that direction
 150 *- if a coordinate is -ve then it will be offset to the
 151 *  left/top of the centre by that many pixels
 152 *- if a coordinate is positive it will be used unchnaged.
 153 *
 154 * @axis:       Input and output coordinate
 155 * @panel_size: Size of panel in pixels for that axis
 156 * @picture_size:       Size of bitmap in pixels for that axis
 157 */
 158static void video_splash_align_axis(int *axis, unsigned long panel_size,
 159                                    unsigned long picture_size)
 160{
 161        unsigned long panel_picture_delta = panel_size - picture_size;
 162        unsigned long axis_alignment;
 163
 164        if (*axis == BMP_ALIGN_CENTER)
 165                axis_alignment = panel_picture_delta / 2;
 166        else if (*axis < 0)
 167                axis_alignment = panel_picture_delta + *axis + 1;
 168        else
 169                return;
 170
 171        *axis = max(0, (int)axis_alignment);
 172}
 173
 174static void video_set_cmap(struct udevice *dev,
 175                           struct bmp_color_table_entry *cte, unsigned colours)
 176{
 177        struct video_priv *priv = dev_get_uclass_priv(dev);
 178        int i;
 179        ushort *cmap = priv->cmap;
 180
 181        debug("%s: colours=%d\n", __func__, colours);
 182        for (i = 0; i < colours; ++i) {
 183                *cmap = ((cte->red   << 8) & 0xf800) |
 184                        ((cte->green << 3) & 0x07e0) |
 185                        ((cte->blue  >> 3) & 0x001f);
 186                cmap++;
 187                cte++;
 188        }
 189}
 190
 191int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
 192                      bool align)
 193{
 194        struct video_priv *priv = dev_get_uclass_priv(dev);
 195        ushort *cmap_base = NULL;
 196        int i, j;
 197        uchar *fb;
 198        struct bmp_image *bmp = map_sysmem(bmp_image, 0);
 199        uchar *bmap;
 200        ushort padded_width;
 201        unsigned long width, height, byte_width;
 202        unsigned long pwidth = priv->xsize;
 203        unsigned colours, bpix, bmp_bpix;
 204        struct bmp_color_table_entry *palette;
 205        int hdr_size;
 206
 207        if (!bmp || !(bmp->header.signature[0] == 'B' &&
 208            bmp->header.signature[1] == 'M')) {
 209                printf("Error: no valid bmp image at %lx\n", bmp_image);
 210
 211                return -EINVAL;
 212        }
 213
 214        width = get_unaligned_le32(&bmp->header.width);
 215        height = get_unaligned_le32(&bmp->header.height);
 216        bmp_bpix = get_unaligned_le16(&bmp->header.bit_count);
 217        hdr_size = get_unaligned_le16(&bmp->header.size);
 218        debug("hdr_size=%d, bmp_bpix=%d\n", hdr_size, bmp_bpix);
 219        palette = (void *)bmp + 14 + hdr_size;
 220
 221        colours = 1 << bmp_bpix;
 222
 223        bpix = VNBITS(priv->bpix);
 224
 225        if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) {
 226                printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
 227                       bpix, bmp_bpix);
 228
 229                return -EINVAL;
 230        }
 231
 232        /*
 233         * We support displaying 8bpp BMPs on 16bpp LCDs
 234         * and displaying 24bpp BMPs on 32bpp LCDs
 235         * */
 236        if (bpix != bmp_bpix &&
 237            !(bmp_bpix == 8 && 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                                *(fb++) = *(bmap++);
 323                                *(fb++) = *(bmap++);
 324                                *(fb++) = *(bmap++);
 325                                *(fb++) = 0;
 326                        }
 327                        fb -= priv->line_length + width * (bpix / 8);
 328                }
 329                break;
 330#endif /* CONFIG_BMP_24BPP */
 331#if defined(CONFIG_BMP_32BPP)
 332        case 32:
 333                for (i = 0; i < height; ++i) {
 334                        for (j = 0; j < width; j++) {
 335                                *(fb++) = *(bmap++);
 336                                *(fb++) = *(bmap++);
 337                                *(fb++) = *(bmap++);
 338                                *(fb++) = *(bmap++);
 339                        }
 340                        fb -= priv->line_length + width * (bpix / 8);
 341                }
 342                break;
 343#endif /* CONFIG_BMP_32BPP */
 344        default:
 345                break;
 346        };
 347
 348        video_sync(dev);
 349
 350        return 0;
 351}
 352
 353