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