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 <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
  21static 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
  30static 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
  41static 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 */
 157static 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
 173static 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
 190int 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 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 == 32)) {
 238                printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
 239                       bpix, get_unaligned_le16(&bmp->header.bit_count));
 240                return -EPERM;
 241        }
 242
 243        debug("Display-bmp: %d x %d  with %d colours, display %d\n",
 244              (int)width, (int)height, (int)colours, 1 << bpix);
 245
 246        if (bmp_bpix == 8)
 247                video_set_cmap(dev, palette, colours);
 248
 249        padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width);
 250
 251        if (align) {
 252                video_splash_align_axis(&x, priv->xsize, width);
 253                video_splash_align_axis(&y, priv->ysize, height);
 254        }
 255
 256        if ((x + width) > pwidth)
 257                width = pwidth - x;
 258        if ((y + height) > priv->ysize)
 259                height = priv->ysize - y;
 260
 261        bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
 262        fb = (uchar *)(priv->fb +
 263                (y + height - 1) * priv->line_length + x * bpix / 8);
 264
 265        switch (bmp_bpix) {
 266        case 1:
 267        case 8: {
 268                cmap_base = priv->cmap;
 269#ifdef CONFIG_VIDEO_BMP_RLE8
 270                u32 compression = get_unaligned_le32(&bmp->header.compression);
 271                debug("compressed %d %d\n", compression, BMP_BI_RLE8);
 272                if (compression == BMP_BI_RLE8) {
 273                        if (bpix != 16) {
 274                                /* TODO implement render code for bpix != 16 */
 275                                printf("Error: only support 16 bpix");
 276                                return -EPROTONOSUPPORT;
 277                        }
 278                        video_display_rle8_bitmap(dev, bmp, cmap_base, fb, x,
 279                                                  y);
 280                        break;
 281                }
 282#endif
 283
 284                if (bpix != 16)
 285                        byte_width = width;
 286                else
 287                        byte_width = width * 2;
 288
 289                for (i = 0; i < height; ++i) {
 290                        WATCHDOG_RESET();
 291                        for (j = 0; j < width; j++) {
 292                                if (bpix != 16) {
 293                                        fb_put_byte(&fb, &bmap);
 294                                } else {
 295                                        *(uint16_t *)fb = cmap_base[*bmap];
 296                                        bmap++;
 297                                        fb += sizeof(uint16_t) / sizeof(*fb);
 298                                }
 299                        }
 300                        bmap += (padded_width - width);
 301                        fb -= byte_width + priv->line_length;
 302                }
 303                break;
 304        }
 305#if defined(CONFIG_BMP_16BPP)
 306        case 16:
 307                for (i = 0; i < height; ++i) {
 308                        WATCHDOG_RESET();
 309                        for (j = 0; j < width; j++)
 310                                fb_put_word(&fb, &bmap);
 311
 312                        bmap += (padded_width - width) * 2;
 313                        fb -= width * 2 + priv->line_length;
 314                }
 315                break;
 316#endif /* CONFIG_BMP_16BPP */
 317#if defined(CONFIG_BMP_24BPP)
 318        case 24:
 319                for (i = 0; i < height; ++i) {
 320                        for (j = 0; j < width; j++) {
 321                                *(fb++) = *(bmap++);
 322                                *(fb++) = *(bmap++);
 323                                *(fb++) = *(bmap++);
 324                                *(fb++) = 0;
 325                        }
 326                        fb -= priv->line_length + width * (bpix / 8);
 327                }
 328                break;
 329#endif /* CONFIG_BMP_24BPP */
 330#if defined(CONFIG_BMP_32BPP)
 331        case 32:
 332                for (i = 0; i < height; ++i) {
 333                        for (j = 0; j < width; j++) {
 334                                *(fb++) = *(bmap++);
 335                                *(fb++) = *(bmap++);
 336                                *(fb++) = *(bmap++);
 337                                *(fb++) = *(bmap++);
 338                        }
 339                        fb -= priv->line_length + width * (bpix / 8);
 340                }
 341                break;
 342#endif /* CONFIG_BMP_32BPP */
 343        default:
 344                break;
 345        };
 346
 347        video_sync(dev, false);
 348
 349        return 0;
 350}
 351
 352