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 <log.h>
  10#include <mapmem.h>
  11#include <splash.h>
  12#include <video.h>
  13#include <watchdog.h>
  14#include <asm/unaligned.h>
  15
  16#ifdef CONFIG_VIDEO_BMP_RLE8
  17#define BMP_RLE8_ESCAPE         0
  18#define BMP_RLE8_EOL            0
  19#define BMP_RLE8_EOBMP          1
  20#define BMP_RLE8_DELTA          2
  21
  22static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap,
  23                                  int cnt)
  24{
  25        while (cnt > 0) {
  26                *(*fbp)++ = cmap[*bmap++];
  27                cnt--;
  28        }
  29}
  30
  31static void draw_encoded_bitmap(ushort **fbp, ushort col, int cnt)
  32{
  33        ushort *fb = *fbp;
  34
  35        while (cnt > 0) {
  36                *fb++ = col;
  37                cnt--;
  38        }
  39        *fbp = fb;
  40}
  41
  42static void video_display_rle8_bitmap(struct udevice *dev,
  43                                      struct bmp_image *bmp, ushort *cmap,
  44                                      uchar *fb, int x_off, int y_off,
  45                                      ulong width, ulong height)
  46{
  47        struct video_priv *priv = dev_get_uclass_priv(dev);
  48        uchar *bmap;
  49        ulong cnt, runlen;
  50        int x, y;
  51        int decode = 1;
  52
  53        debug("%s\n", __func__);
  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/**
 144 * video_splash_align_axis() - Align a single coordinate
 145 *
 146 *- if a coordinate is 0x7fff then the image will be centred in
 147 *  that direction
 148 *- if a coordinate is -ve then it will be offset to the
 149 *  left/top of the centre by that many pixels
 150 *- if a coordinate is positive it will be used unchnaged.
 151 *
 152 * @axis:       Input and output coordinate
 153 * @panel_size: Size of panel in pixels for that axis
 154 * @picture_size:       Size of bitmap in pixels for that axis
 155 */
 156static void video_splash_align_axis(int *axis, unsigned long panel_size,
 157                                    unsigned long picture_size)
 158{
 159        long panel_picture_delta = panel_size - picture_size;
 160        long axis_alignment;
 161
 162        if (*axis == BMP_ALIGN_CENTER)
 163                axis_alignment = panel_picture_delta / 2;
 164        else if (*axis < 0)
 165                axis_alignment = panel_picture_delta + *axis + 1;
 166        else
 167                return;
 168
 169        *axis = max(0, (int)axis_alignment);
 170}
 171
 172static void video_set_cmap(struct udevice *dev,
 173                           struct bmp_color_table_entry *cte, unsigned colours)
 174{
 175        struct video_priv *priv = dev_get_uclass_priv(dev);
 176        int i;
 177        ushort *cmap = priv->cmap;
 178
 179        debug("%s: colours=%d\n", __func__, colours);
 180        for (i = 0; i < colours; ++i) {
 181                *cmap = ((cte->red   << 8) & 0xf800) |
 182                        ((cte->green << 3) & 0x07e0) |
 183                        ((cte->blue  >> 3) & 0x001f);
 184                cmap++;
 185                cte++;
 186        }
 187}
 188
 189int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
 190                      bool align)
 191{
 192        struct video_priv *priv = dev_get_uclass_priv(dev);
 193        ushort *cmap_base = NULL;
 194        int i, j;
 195        uchar *start, *fb;
 196        struct bmp_image *bmp = map_sysmem(bmp_image, 0);
 197        uchar *bmap;
 198        ushort padded_width;
 199        unsigned long width, height, byte_width;
 200        unsigned long pwidth = priv->xsize;
 201        unsigned colours, bpix, bmp_bpix;
 202        struct bmp_color_table_entry *palette;
 203        int hdr_size;
 204        int ret;
 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 == 8 && bpix == 24) &&
 238            !(bmp_bpix == 8 && bpix == 32) &&
 239            !(bmp_bpix == 24 && bpix == 16) &&
 240            !(bmp_bpix == 24 && bpix == 32)) {
 241                printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
 242                       bpix, get_unaligned_le16(&bmp->header.bit_count));
 243                return -EPERM;
 244        }
 245
 246        debug("Display-bmp: %d x %d  with %d colours, display %d\n",
 247              (int)width, (int)height, (int)colours, 1 << bpix);
 248
 249        if (bmp_bpix == 8)
 250                video_set_cmap(dev, palette, colours);
 251
 252        padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width);
 253
 254        if (align) {
 255                video_splash_align_axis(&x, priv->xsize, width);
 256                video_splash_align_axis(&y, priv->ysize, height);
 257        }
 258
 259        if ((x + width) > pwidth)
 260                width = pwidth - x;
 261        if ((y + height) > priv->ysize)
 262                height = priv->ysize - y;
 263
 264        bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
 265        start = (uchar *)(priv->fb +
 266                (y + height) * priv->line_length + x * bpix / 8);
 267
 268        /* Move back to the final line to be drawn */
 269        fb = start - priv->line_length;
 270
 271        switch (bmp_bpix) {
 272        case 1:
 273        case 8: {
 274                struct bmp_color_table_entry *cte;
 275                cmap_base = priv->cmap;
 276#ifdef CONFIG_VIDEO_BMP_RLE8
 277                u32 compression = get_unaligned_le32(&bmp->header.compression);
 278                debug("compressed %d %d\n", compression, BMP_BI_RLE8);
 279                if (compression == BMP_BI_RLE8) {
 280                        if (bpix != 16) {
 281                                /* TODO implement render code for bpix != 16 */
 282                                printf("Error: only support 16 bpix");
 283                                return -EPROTONOSUPPORT;
 284                        }
 285                        video_display_rle8_bitmap(dev, bmp, cmap_base, fb, x,
 286                                                  y, width, height);
 287                        break;
 288                }
 289#endif
 290                byte_width = width * (bpix / 8);
 291                if (!byte_width)
 292                        byte_width = width;
 293
 294                for (i = 0; i < height; ++i) {
 295                        WATCHDOG_RESET();
 296                        for (j = 0; j < width; j++) {
 297                                if (bpix == 8) {
 298                                        fb_put_byte(&fb, &bmap);
 299                                } else if (bpix == 16) {
 300                                        *(uint16_t *)fb = cmap_base[*bmap];
 301                                        bmap++;
 302                                        fb += sizeof(uint16_t) / sizeof(*fb);
 303                                } else {
 304                                        /* Only support big endian */
 305                                        cte = &palette[*bmap];
 306                                        bmap++;
 307                                        if (bpix == 24) {
 308                                                *(fb++) = cte->red;
 309                                                *(fb++) = cte->green;
 310                                                *(fb++) = cte->blue;
 311                                        } else {
 312                                                *(fb++) = cte->blue;
 313                                                *(fb++) = cte->green;
 314                                                *(fb++) = cte->red;
 315                                                *(fb++) = 0;
 316                                        }
 317                                }
 318                        }
 319                        bmap += (padded_width - width);
 320                        fb -= byte_width + priv->line_length;
 321                }
 322                break;
 323        }
 324#if defined(CONFIG_BMP_16BPP)
 325        case 16:
 326                for (i = 0; i < height; ++i) {
 327                        WATCHDOG_RESET();
 328                        for (j = 0; j < width; j++)
 329                                fb_put_word(&fb, &bmap);
 330
 331                        bmap += (padded_width - width);
 332                        fb -= width * 2 + priv->line_length;
 333                }
 334                break;
 335#endif /* CONFIG_BMP_16BPP */
 336#if defined(CONFIG_BMP_24BPP)
 337        case 24:
 338                for (i = 0; i < height; ++i) {
 339                        for (j = 0; j < width; j++) {
 340                                if (bpix == 16) {
 341                                        /* 16bit 555RGB format */
 342                                        *(u16 *)fb = ((bmap[2] >> 3) << 10) |
 343                                                ((bmap[1] >> 3) << 5) |
 344                                                (bmap[0] >> 3);
 345                                        bmap += 3;
 346                                        fb += 2;
 347                                } else {
 348                                        *(fb++) = *(bmap++);
 349                                        *(fb++) = *(bmap++);
 350                                        *(fb++) = *(bmap++);
 351                                        *(fb++) = 0;
 352                                }
 353                        }
 354                        fb -= priv->line_length + width * (bpix / 8);
 355                        bmap += (padded_width - width);
 356                }
 357                break;
 358#endif /* CONFIG_BMP_24BPP */
 359#if defined(CONFIG_BMP_32BPP)
 360        case 32:
 361                for (i = 0; i < height; ++i) {
 362                        for (j = 0; j < width; j++) {
 363                                *(fb++) = *(bmap++);
 364                                *(fb++) = *(bmap++);
 365                                *(fb++) = *(bmap++);
 366                                *(fb++) = *(bmap++);
 367                        }
 368                        fb -= priv->line_length + width * (bpix / 8);
 369                }
 370                break;
 371#endif /* CONFIG_BMP_32BPP */
 372        default:
 373                break;
 374        };
 375
 376        /* Find the position of the top left of the image in the framebuffer */
 377        fb = (uchar *)(priv->fb + y * priv->line_length + x * bpix / 8);
 378        ret = video_sync_copy(dev, start, fb);
 379        if (ret)
 380                return log_ret(ret);
 381
 382        return video_sync(dev, false);
 383}
 384