uboot/drivers/video/fsl_dcu_fb.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2014 Freescale Semiconductor, Inc.
   4 * Copyright 2019 Toradex AG
   5 *
   6 * FSL DCU Framebuffer driver
   7 */
   8
   9#include <init.h>
  10#include <asm/io.h>
  11#include <common.h>
  12#include <dm.h>
  13#include <fdt_support.h>
  14#include <fsl_dcu_fb.h>
  15#include <linux/fb.h>
  16#include <malloc.h>
  17#include <video.h>
  18#include <video_fb.h>
  19#include "videomodes.h"
  20
  21/* Convert the X,Y resolution pair into a single number */
  22#define RESOLUTION(x, y) (((u32)(x) << 16) | (y))
  23
  24#ifdef CONFIG_SYS_FSL_DCU_LE
  25#define dcu_read32      in_le32
  26#define dcu_write32     out_le32
  27#elif defined(CONFIG_SYS_FSL_DCU_BE)
  28#define dcu_read32      in_be32
  29#define dcu_write32     out_be32
  30#endif
  31
  32#define DCU_MODE_BLEND_ITER(x)          ((x) << 20)
  33#define DCU_MODE_RASTER_EN              (1 << 14)
  34#define DCU_MODE_NORMAL                 1
  35#define DCU_MODE_COLORBAR               3
  36#define DCU_BGND_R(x)                   ((x) << 16)
  37#define DCU_BGND_G(x)                   ((x) << 8)
  38#define DCU_BGND_B(x)                   (x)
  39#define DCU_DISP_SIZE_DELTA_Y(x)        ((x) << 16)
  40#define DCU_DISP_SIZE_DELTA_X(x)        (x)
  41#define DCU_HSYN_PARA_BP(x)             ((x) << 22)
  42#define DCU_HSYN_PARA_PW(x)             ((x) << 11)
  43#define DCU_HSYN_PARA_FP(x)             (x)
  44#define DCU_VSYN_PARA_BP(x)             ((x) << 22)
  45#define DCU_VSYN_PARA_PW(x)             ((x) << 11)
  46#define DCU_VSYN_PARA_FP(x)             (x)
  47#define DCU_SYN_POL_INV_PXCK_FALL       (1 << 6)
  48#define DCU_SYN_POL_NEG_REMAIN          (0 << 5)
  49#define DCU_SYN_POL_INV_VS_LOW          (1 << 1)
  50#define DCU_SYN_POL_INV_HS_LOW          (1)
  51#define DCU_THRESHOLD_LS_BF_VS(x)       ((x) << 16)
  52#define DCU_THRESHOLD_OUT_BUF_HIGH(x)   ((x) << 8)
  53#define DCU_THRESHOLD_OUT_BUF_LOW(x)    (x)
  54#define DCU_UPDATE_MODE_MODE            (1 << 31)
  55#define DCU_UPDATE_MODE_READREG         (1 << 30)
  56
  57#define DCU_CTRLDESCLN_1_HEIGHT(x)      ((x) << 16)
  58#define DCU_CTRLDESCLN_1_WIDTH(x)       (x)
  59#define DCU_CTRLDESCLN_2_POSY(x)        ((x) << 16)
  60#define DCU_CTRLDESCLN_2_POSX(x)        (x)
  61#define DCU_CTRLDESCLN_4_EN             (1 << 31)
  62#define DCU_CTRLDESCLN_4_TILE_EN        (1 << 30)
  63#define DCU_CTRLDESCLN_4_DATA_SEL_CLUT  (1 << 29)
  64#define DCU_CTRLDESCLN_4_SAFETY_EN      (1 << 28)
  65#define DCU_CTRLDESCLN_4_TRANS(x)       ((x) << 20)
  66#define DCU_CTRLDESCLN_4_BPP(x)         ((x) << 16)
  67#define DCU_CTRLDESCLN_4_RLE_EN         (1 << 15)
  68#define DCU_CTRLDESCLN_4_LUOFFS(x)      ((x) << 4)
  69#define DCU_CTRLDESCLN_4_BB_ON          (1 << 2)
  70#define DCU_CTRLDESCLN_4_AB(x)          (x)
  71#define DCU_CTRLDESCLN_5_CKMAX_R(x)     ((x) << 16)
  72#define DCU_CTRLDESCLN_5_CKMAX_G(x)     ((x) << 8)
  73#define DCU_CTRLDESCLN_5_CKMAX_B(x)     (x)
  74#define DCU_CTRLDESCLN_6_CKMIN_R(x)     ((x) << 16)
  75#define DCU_CTRLDESCLN_6_CKMIN_G(x)     ((x) << 8)
  76#define DCU_CTRLDESCLN_6_CKMIN_B(x)     (x)
  77#define DCU_CTRLDESCLN_7_TILE_VER(x)    ((x) << 16)
  78#define DCU_CTRLDESCLN_7_TILE_HOR(x)    (x)
  79#define DCU_CTRLDESCLN_8_FG_FCOLOR(x)   (x)
  80#define DCU_CTRLDESCLN_9_BG_BCOLOR(x)   (x)
  81
  82#define BPP_16_RGB565                   4
  83#define BPP_24_RGB888                   5
  84#define BPP_32_ARGB8888                 6
  85
  86DECLARE_GLOBAL_DATA_PTR;
  87
  88/*
  89 * This setting is used for the TWR_LCD_RGB card
  90 */
  91static struct fb_videomode fsl_dcu_mode_480_272 = {
  92        .name           = "480x272-60",
  93        .refresh        = 60,
  94        .xres           = 480,
  95        .yres           = 272,
  96        .pixclock       = 91996,
  97        .left_margin    = 2,
  98        .right_margin   = 2,
  99        .upper_margin   = 1,
 100        .lower_margin   = 1,
 101        .hsync_len      = 41,
 102        .vsync_len      = 2,
 103        .sync           = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
 104        .vmode          = FB_VMODE_NONINTERLACED
 105};
 106
 107/*
 108 * This setting is used for Siliconimage SiI9022A HDMI
 109 */
 110static struct fb_videomode fsl_dcu_cea_mode_640_480 = {
 111        .name           = "640x480-60",
 112        .refresh        = 60,
 113        .xres           = 640,
 114        .yres           = 480,
 115        .pixclock       = 39722,
 116        .left_margin    = 48,
 117        .right_margin   = 16,
 118        .upper_margin   = 33,
 119        .lower_margin   = 10,
 120        .hsync_len      = 96,
 121        .vsync_len      = 2,
 122        .sync           = 0,
 123        .vmode          = FB_VMODE_NONINTERLACED,
 124};
 125
 126static struct fb_videomode fsl_dcu_mode_640_480 = {
 127        .name           = "640x480-60",
 128        .refresh        = 60,
 129        .xres           = 640,
 130        .yres           = 480,
 131        .pixclock       = 25175,
 132        .left_margin    = 40,
 133        .right_margin   = 24,
 134        .upper_margin   = 32,
 135        .lower_margin   = 11,
 136        .hsync_len      = 96,
 137        .vsync_len      = 2,
 138        .sync           = 0,
 139        .vmode          = FB_VMODE_NONINTERLACED,
 140};
 141
 142static struct fb_videomode fsl_dcu_mode_800_480 = {
 143        .name           = "800x480-60",
 144        .refresh        = 60,
 145        .xres           = 800,
 146        .yres           = 480,
 147        .pixclock       = 33260,
 148        .left_margin    = 216,
 149        .right_margin   = 40,
 150        .upper_margin   = 35,
 151        .lower_margin   = 10,
 152        .hsync_len      = 128,
 153        .vsync_len      = 2,
 154        .sync           = 0,
 155        .vmode          = FB_VMODE_NONINTERLACED,
 156};
 157
 158static struct fb_videomode fsl_dcu_mode_1024_600 = {
 159        .name           = "1024x600-60",
 160        .refresh        = 60,
 161        .xres           = 1024,
 162        .yres           = 600,
 163        .pixclock       = 48000,
 164        .left_margin    = 104,
 165        .right_margin   = 43,
 166        .upper_margin   = 24,
 167        .lower_margin   = 20,
 168        .hsync_len      = 5,
 169        .vsync_len      = 5,
 170        .sync           = 0,
 171        .vmode          = FB_VMODE_NONINTERLACED,
 172};
 173
 174/*
 175 * DCU register map
 176 */
 177struct dcu_reg {
 178        u32 desc_cursor[4];
 179        u32 mode;
 180        u32 bgnd;
 181        u32 disp_size;
 182        u32 hsyn_para;
 183        u32 vsyn_para;
 184        u32 synpol;
 185        u32 threshold;
 186        u32 int_status;
 187        u32 int_mask;
 188        u32 colbar[8];
 189        u32 div_ratio;
 190        u32 sign_calc[2];
 191        u32 crc_val;
 192        u8 res_064[0x6c-0x64];
 193        u32 parr_err_status1;
 194        u8 res_070[0x7c-0x70];
 195        u32 parr_err_status3;
 196        u32 mparr_err_status1;
 197        u8 res_084[0x90-0x84];
 198        u32 mparr_err_status3;
 199        u32 threshold_inp_buf[2];
 200        u8 res_09c[0xa0-0x9c];
 201        u32 luma_comp;
 202        u32 chroma_red;
 203        u32 chroma_green;
 204        u32 chroma_blue;
 205        u32 crc_pos;
 206        u32 lyr_intpol_en;
 207        u32 lyr_luma_comp;
 208        u32 lyr_chrm_red;
 209        u32 lyr_chrm_grn;
 210        u32 lyr_chrm_blue;
 211        u8 res_0c4[0xcc-0xc8];
 212        u32 update_mode;
 213        u32 underrun;
 214        u8 res_0d4[0x100-0xd4];
 215        u32 gpr;
 216        u32 slr_l[2];
 217        u32 slr_disp_size;
 218        u32 slr_hvsync_para;
 219        u32 slr_pol;
 220        u32 slr_l_transp[2];
 221        u8 res_120[0x200-0x120];
 222        u32 ctrldescl[DCU_LAYER_MAX_NUM][16];
 223};
 224
 225static void reset_total_layers(void)
 226{
 227        struct dcu_reg *regs = (struct dcu_reg *)CONFIG_SYS_DCU_ADDR;
 228        int i;
 229
 230        for (i = 0; i < DCU_LAYER_MAX_NUM; i++) {
 231                dcu_write32(&regs->ctrldescl[i][0], 0);
 232                dcu_write32(&regs->ctrldescl[i][1], 0);
 233                dcu_write32(&regs->ctrldescl[i][2], 0);
 234                dcu_write32(&regs->ctrldescl[i][3], 0);
 235                dcu_write32(&regs->ctrldescl[i][4], 0);
 236                dcu_write32(&regs->ctrldescl[i][5], 0);
 237                dcu_write32(&regs->ctrldescl[i][6], 0);
 238                dcu_write32(&regs->ctrldescl[i][7], 0);
 239                dcu_write32(&regs->ctrldescl[i][8], 0);
 240                dcu_write32(&regs->ctrldescl[i][9], 0);
 241                dcu_write32(&regs->ctrldescl[i][10], 0);
 242        }
 243}
 244
 245static int layer_ctrldesc_init(struct fb_info fbinfo,
 246                               int index, u32 pixel_format)
 247{
 248        struct dcu_reg *regs = (struct dcu_reg *)CONFIG_SYS_DCU_ADDR;
 249        unsigned int bpp = BPP_24_RGB888;
 250
 251        dcu_write32(&regs->ctrldescl[index][0],
 252                    DCU_CTRLDESCLN_1_HEIGHT(fbinfo.var.yres) |
 253                    DCU_CTRLDESCLN_1_WIDTH(fbinfo.var.xres));
 254
 255        dcu_write32(&regs->ctrldescl[index][1],
 256                    DCU_CTRLDESCLN_2_POSY(0) |
 257                    DCU_CTRLDESCLN_2_POSX(0));
 258
 259        dcu_write32(&regs->ctrldescl[index][2],
 260                    (unsigned int)fbinfo.screen_base);
 261
 262        switch (pixel_format) {
 263        case 16:
 264                bpp = BPP_16_RGB565;
 265                break;
 266        case 24:
 267                bpp = BPP_24_RGB888;
 268                break;
 269        case 32:
 270                bpp = BPP_32_ARGB8888;
 271                break;
 272        default:
 273                printf("unsupported color depth: %u\n", pixel_format);
 274        }
 275
 276        dcu_write32(&regs->ctrldescl[index][3],
 277                    DCU_CTRLDESCLN_4_EN |
 278                    DCU_CTRLDESCLN_4_TRANS(0xff) |
 279                    DCU_CTRLDESCLN_4_BPP(bpp) |
 280                    DCU_CTRLDESCLN_4_AB(0));
 281
 282        dcu_write32(&regs->ctrldescl[index][4],
 283                    DCU_CTRLDESCLN_5_CKMAX_R(0xff) |
 284                    DCU_CTRLDESCLN_5_CKMAX_G(0xff) |
 285                    DCU_CTRLDESCLN_5_CKMAX_B(0xff));
 286        dcu_write32(&regs->ctrldescl[index][5],
 287                    DCU_CTRLDESCLN_6_CKMIN_R(0) |
 288                    DCU_CTRLDESCLN_6_CKMIN_G(0) |
 289                    DCU_CTRLDESCLN_6_CKMIN_B(0));
 290
 291        dcu_write32(&regs->ctrldescl[index][6],
 292                    DCU_CTRLDESCLN_7_TILE_VER(0) |
 293                    DCU_CTRLDESCLN_7_TILE_HOR(0));
 294
 295        dcu_write32(&regs->ctrldescl[index][7], DCU_CTRLDESCLN_8_FG_FCOLOR(0));
 296        dcu_write32(&regs->ctrldescl[index][8], DCU_CTRLDESCLN_9_BG_BCOLOR(0));
 297
 298        return 0;
 299}
 300
 301int fsl_dcu_init(struct fb_info *fbinfo, unsigned int xres,
 302                 unsigned int yres, unsigned int pixel_format)
 303{
 304        struct dcu_reg *regs = (struct dcu_reg *)CONFIG_SYS_DCU_ADDR;
 305        unsigned int div, mode;
 306/*
 307 * When DM_VIDEO is enabled reservation of framebuffer is done
 308 * in advance during bind() call.
 309 */
 310#if !CONFIG_IS_ENABLED(DM_VIDEO)
 311        fbinfo->screen_size = fbinfo->var.xres * fbinfo->var.yres *
 312                             (fbinfo->var.bits_per_pixel / 8);
 313
 314        if (fbinfo->screen_size > CONFIG_VIDEO_FSL_DCU_MAX_FB_SIZE_MB) {
 315                fbinfo->screen_size = 0;
 316                return -ENOMEM;
 317        }
 318        /* Reserve framebuffer at the end of memory */
 319        gd->fb_base = gd->bd->bi_dram[0].start +
 320                        gd->bd->bi_dram[0].size - fbinfo->screen_size;
 321        fbinfo->screen_base = (char *)gd->fb_base;
 322
 323        memset(fbinfo->screen_base, 0, fbinfo->screen_size);
 324#endif
 325
 326        reset_total_layers();
 327
 328        dcu_write32(&regs->disp_size,
 329                    DCU_DISP_SIZE_DELTA_Y(fbinfo->var.yres) |
 330                    DCU_DISP_SIZE_DELTA_X(fbinfo->var.xres / 16));
 331
 332        dcu_write32(&regs->hsyn_para,
 333                    DCU_HSYN_PARA_BP(fbinfo->var.left_margin) |
 334                    DCU_HSYN_PARA_PW(fbinfo->var.hsync_len) |
 335                    DCU_HSYN_PARA_FP(fbinfo->var.right_margin));
 336
 337        dcu_write32(&regs->vsyn_para,
 338                    DCU_VSYN_PARA_BP(fbinfo->var.upper_margin) |
 339                    DCU_VSYN_PARA_PW(fbinfo->var.vsync_len) |
 340                    DCU_VSYN_PARA_FP(fbinfo->var.lower_margin));
 341
 342        dcu_write32(&regs->synpol,
 343                    DCU_SYN_POL_INV_PXCK_FALL |
 344                    DCU_SYN_POL_NEG_REMAIN |
 345                    DCU_SYN_POL_INV_VS_LOW |
 346                    DCU_SYN_POL_INV_HS_LOW);
 347
 348        dcu_write32(&regs->bgnd,
 349                    DCU_BGND_R(0) | DCU_BGND_G(0) | DCU_BGND_B(0));
 350
 351        dcu_write32(&regs->mode,
 352                    DCU_MODE_BLEND_ITER(2) |
 353                    DCU_MODE_RASTER_EN);
 354
 355        dcu_write32(&regs->threshold,
 356                    DCU_THRESHOLD_LS_BF_VS(0x3) |
 357                    DCU_THRESHOLD_OUT_BUF_HIGH(0x78) |
 358                    DCU_THRESHOLD_OUT_BUF_LOW(0));
 359
 360        mode = dcu_read32(&regs->mode);
 361        dcu_write32(&regs->mode, mode | DCU_MODE_NORMAL);
 362
 363        layer_ctrldesc_init(*fbinfo, 0, pixel_format);
 364
 365        div = dcu_set_pixel_clock(fbinfo->var.pixclock);
 366        dcu_write32(&regs->div_ratio, (div - 1));
 367
 368        dcu_write32(&regs->update_mode, DCU_UPDATE_MODE_READREG);
 369
 370        return 0;
 371}
 372
 373ulong board_get_usable_ram_top(ulong total_size)
 374{
 375        return gd->ram_top - CONFIG_VIDEO_FSL_DCU_MAX_FB_SIZE_MB;
 376}
 377
 378int fsl_probe_common(struct fb_info *fbinfo, unsigned int *win_x,
 379                     unsigned int *win_y)
 380{
 381        const char *options;
 382        unsigned int depth = 0, freq = 0;
 383
 384        struct fb_videomode *fsl_dcu_mode_db = &fsl_dcu_mode_480_272;
 385
 386        if (!video_get_video_mode(win_x, win_y, &depth, &freq,
 387                                  &options))
 388                return -EINVAL;
 389
 390        /* Find the monitor port, which is a required option */
 391        if (!options)
 392                return -EINVAL;
 393
 394        if (strncmp(options, "monitor=", 8) != 0)
 395                return -EINVAL;
 396
 397        switch (RESOLUTION(*win_x, *win_y)) {
 398        case RESOLUTION(480, 272):
 399                fsl_dcu_mode_db = &fsl_dcu_mode_480_272;
 400                break;
 401        case RESOLUTION(640, 480):
 402                if (!strncmp(options, "monitor=hdmi", 12))
 403                        fsl_dcu_mode_db = &fsl_dcu_cea_mode_640_480;
 404                else
 405                        fsl_dcu_mode_db = &fsl_dcu_mode_640_480;
 406                break;
 407        case RESOLUTION(800, 480):
 408                fsl_dcu_mode_db = &fsl_dcu_mode_800_480;
 409                break;
 410        case RESOLUTION(1024, 600):
 411                fsl_dcu_mode_db = &fsl_dcu_mode_1024_600;
 412                break;
 413        default:
 414                printf("unsupported resolution %ux%u\n",
 415                       *win_x, *win_y);
 416        }
 417
 418        fbinfo->var.xres = fsl_dcu_mode_db->xres;
 419        fbinfo->var.yres = fsl_dcu_mode_db->yres;
 420        fbinfo->var.bits_per_pixel = 32;
 421        fbinfo->var.pixclock = fsl_dcu_mode_db->pixclock;
 422        fbinfo->var.left_margin = fsl_dcu_mode_db->left_margin;
 423        fbinfo->var.right_margin = fsl_dcu_mode_db->right_margin;
 424        fbinfo->var.upper_margin = fsl_dcu_mode_db->upper_margin;
 425        fbinfo->var.lower_margin = fsl_dcu_mode_db->lower_margin;
 426        fbinfo->var.hsync_len = fsl_dcu_mode_db->hsync_len;
 427        fbinfo->var.vsync_len = fsl_dcu_mode_db->vsync_len;
 428        fbinfo->var.sync = fsl_dcu_mode_db->sync;
 429        fbinfo->var.vmode = fsl_dcu_mode_db->vmode;
 430        fbinfo->fix.line_length = fbinfo->var.xres *
 431                                  fbinfo->var.bits_per_pixel / 8;
 432
 433        return platform_dcu_init(fbinfo, *win_x, *win_y,
 434                                 options + 8, fsl_dcu_mode_db);
 435}
 436
 437#ifndef CONFIG_DM_VIDEO
 438static struct fb_info info;
 439
 440#if defined(CONFIG_OF_BOARD_SETUP)
 441int fsl_dcu_fixedfb_setup(void *blob)
 442{
 443        u64 start, size;
 444        int ret;
 445
 446        start = gd->bd->bi_dram[0].start;
 447        size = gd->bd->bi_dram[0].size - info.screen_size;
 448
 449        /*
 450         * Align size on section size (1 MiB).
 451         */
 452        size &= 0xfff00000;
 453        ret = fdt_fixup_memory_banks(blob, &start, &size, 1);
 454        if (ret) {
 455                eprintf("Cannot setup fb: Error reserving memory\n");
 456                return ret;
 457        }
 458
 459        return 0;
 460}
 461#endif
 462
 463void *video_hw_init(void)
 464{
 465        static GraphicDevice ctfb;
 466
 467        if (fsl_probe_common(&info, &ctfb.winSizeX, &ctfb.winSizeY) < 0)
 468                return NULL;
 469
 470        ctfb.frameAdrs = (unsigned int)info.screen_base;
 471        ctfb.plnSizeX = ctfb.winSizeX;
 472        ctfb.plnSizeY = ctfb.winSizeY;
 473
 474        ctfb.gdfBytesPP = 4;
 475        ctfb.gdfIndex = GDF_32BIT_X888RGB;
 476
 477        ctfb.memSize = info.screen_size;
 478
 479        return &ctfb;
 480}
 481
 482#else /* ifndef CONFIG_DM_VIDEO */
 483
 484static int fsl_dcu_video_probe(struct udevice *dev)
 485{
 486        struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
 487        struct video_priv *uc_priv = dev_get_uclass_priv(dev);
 488        struct fb_info fbinfo = { 0 };
 489        unsigned int win_x;
 490        unsigned int win_y;
 491        u32 fb_start, fb_end;
 492        int ret = 0;
 493
 494        fb_start = plat->base & ~(MMU_SECTION_SIZE - 1);
 495        fb_end = plat->base + plat->size;
 496        fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT);
 497
 498        fbinfo.screen_base = (char *)fb_start;
 499        fbinfo.screen_size = plat->size;
 500
 501        ret = fsl_probe_common(&fbinfo, &win_x, &win_y);
 502        if (ret < 0)
 503                return ret;
 504
 505        uc_priv->bpix = VIDEO_BPP32;
 506        uc_priv->xsize = win_x;
 507        uc_priv->ysize = win_y;
 508
 509        /* Enable dcache for the frame buffer */
 510        mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start,
 511                                        DCACHE_WRITEBACK);
 512        video_set_flush_dcache(dev, true);
 513        return ret;
 514}
 515
 516static int fsl_dcu_video_bind(struct udevice *dev)
 517{
 518        struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
 519        unsigned int win_x;
 520        unsigned int win_y;
 521        unsigned int depth = 0, freq = 0;
 522        const char *options;
 523        int ret = 0;
 524
 525        ret = video_get_video_mode(&win_x, &win_y, &depth, &freq, &options);
 526        if (ret < 0)
 527                return ret;
 528
 529        plat->size = win_x * win_y * 32;
 530
 531        return 0;
 532}
 533
 534static const struct udevice_id fsl_dcu_video_ids[] = {
 535        { .compatible = "fsl,vf610-dcu" },
 536        { /* sentinel */ }
 537};
 538
 539U_BOOT_DRIVER(fsl_dcu_video) = {
 540        .name   = "fsl_dcu_video",
 541        .id     = UCLASS_VIDEO,
 542        .of_match = fsl_dcu_video_ids,
 543        .bind   = fsl_dcu_video_bind,
 544        .probe  = fsl_dcu_video_probe,
 545        .flags  = DM_FLAG_PRE_RELOC,
 546};
 547#endif /* ifndef CONFIG_DM_VIDEO */
 548