/* * Copyright 2014 Freescale Semiconductor, Inc. * * FSL DCU Framebuffer driver * * SPDX-License-Identifier: GPL-2.0+ */ #include #include #include #include #include #include #include "videomodes.h" /* Convert the X,Y resolution pair into a single number */ #define RESOLUTION(x, y) (((u32)(x) << 16) | (y)) #ifdef CONFIG_SYS_FSL_DCU_LE #define dcu_read32 in_le32 #define dcu_write32 out_le32 #elif defined(CONFIG_SYS_FSL_DCU_BE) #define dcu_read32 in_be32 #define dcu_write32 out_be32 #endif #define DCU_MODE_BLEND_ITER(x) ((x) << 20) #define DCU_MODE_RASTER_EN (1 << 14) #define DCU_MODE_NORMAL 1 #define DCU_MODE_COLORBAR 3 #define DCU_BGND_R(x) ((x) << 16) #define DCU_BGND_G(x) ((x) << 8) #define DCU_BGND_B(x) (x) #define DCU_DISP_SIZE_DELTA_Y(x) ((x) << 16) #define DCU_DISP_SIZE_DELTA_X(x) (x) #define DCU_HSYN_PARA_BP(x) ((x) << 22) #define DCU_HSYN_PARA_PW(x) ((x) << 11) #define DCU_HSYN_PARA_FP(x) (x) #define DCU_VSYN_PARA_BP(x) ((x) << 22) #define DCU_VSYN_PARA_PW(x) ((x) << 11) #define DCU_VSYN_PARA_FP(x) (x) #define DCU_SYN_POL_INV_PXCK_FALL (0 << 6) #define DCU_SYN_POL_NEG_REMAIN (0 << 5) #define DCU_SYN_POL_INV_VS_LOW (1 << 1) #define DCU_SYN_POL_INV_HS_LOW (1) #define DCU_THRESHOLD_LS_BF_VS(x) ((x) << 16) #define DCU_THRESHOLD_OUT_BUF_HIGH(x) ((x) << 8) #define DCU_THRESHOLD_OUT_BUF_LOW(x) (x) #define DCU_UPDATE_MODE_MODE (1 << 31) #define DCU_UPDATE_MODE_READREG (1 << 30) #define DCU_CTRLDESCLN_1_HEIGHT(x) ((x) << 16) #define DCU_CTRLDESCLN_1_WIDTH(x) (x) #define DCU_CTRLDESCLN_2_POSY(x) ((x) << 16) #define DCU_CTRLDESCLN_2_POSX(x) (x) #define DCU_CTRLDESCLN_4_EN (1 << 31) #define DCU_CTRLDESCLN_4_TILE_EN (1 << 30) #define DCU_CTRLDESCLN_4_DATA_SEL_CLUT (1 << 29) #define DCU_CTRLDESCLN_4_SAFETY_EN (1 << 28) #define DCU_CTRLDESCLN_4_TRANS(x) ((x) << 20) #define DCU_CTRLDESCLN_4_BPP(x) ((x) << 16) #define DCU_CTRLDESCLN_4_RLE_EN (1 << 15) #define DCU_CTRLDESCLN_4_LUOFFS(x) ((x) << 4) #define DCU_CTRLDESCLN_4_BB_ON (1 << 2) #define DCU_CTRLDESCLN_4_AB(x) (x) #define DCU_CTRLDESCLN_5_CKMAX_R(x) ((x) << 16) #define DCU_CTRLDESCLN_5_CKMAX_G(x) ((x) << 8) #define DCU_CTRLDESCLN_5_CKMAX_B(x) (x) #define DCU_CTRLDESCLN_6_CKMIN_R(x) ((x) << 16) #define DCU_CTRLDESCLN_6_CKMIN_G(x) ((x) << 8) #define DCU_CTRLDESCLN_6_CKMIN_B(x) (x) #define DCU_CTRLDESCLN_7_TILE_VER(x) ((x) << 16) #define DCU_CTRLDESCLN_7_TILE_HOR(x) (x) #define DCU_CTRLDESCLN_8_FG_FCOLOR(x) (x) #define DCU_CTRLDESCLN_9_BG_BCOLOR(x) (x) #define BPP_16_RGB565 4 #define BPP_24_RGB888 5 #define BPP_32_ARGB8888 6 /* * This setting is used for the TWR_LCD_RGB card */ static struct fb_videomode fsl_dcu_mode_480_272 = { .name = "480x272-60", .refresh = 60, .xres = 480, .yres = 272, .pixclock = 91996, .left_margin = 2, .right_margin = 2, .upper_margin = 1, .lower_margin = 1, .hsync_len = 41, .vsync_len = 2, .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED }; /* * This setting is used for Siliconimage SiI9022A HDMI */ static struct fb_videomode fsl_dcu_mode_640_480 = { .name = "640x480-60", .refresh = 60, .xres = 640, .yres = 480, .pixclock = 39722, .left_margin = 48, .right_margin = 16, .upper_margin = 33, .lower_margin = 10, .hsync_len = 96, .vsync_len = 2, .sync = 0, .vmode = FB_VMODE_NONINTERLACED, }; /* * DCU register map */ struct dcu_reg { u32 desc_cursor[4]; u32 mode; u32 bgnd; u32 disp_size; u32 hsyn_para; u32 vsyn_para; u32 synpol; u32 threshold; u32 int_status; u32 int_mask; u32 colbar[8]; u32 div_ratio; u32 sign_calc[2]; u32 crc_val; u8 res_064[0x6c-0x64]; u32 parr_err_status1; u8 res_070[0x7c-0x70]; u32 parr_err_status3; u32 mparr_err_status1; u8 res_084[0x90-0x84]; u32 mparr_err_status3; u32 threshold_inp_buf[2]; u8 res_09c[0xa0-0x9c]; u32 luma_comp; u32 chroma_red; u32 chroma_green; u32 chroma_blue; u32 crc_pos; u32 lyr_intpol_en; u32 lyr_luma_comp; u32 lyr_chrm_red; u32 lyr_chrm_grn; u32 lyr_chrm_blue; u8 res_0c4[0xcc-0xc8]; u32 update_mode; u32 underrun; u8 res_0d4[0x100-0xd4]; u32 gpr; u32 slr_l[2]; u32 slr_disp_size; u32 slr_hvsync_para; u32 slr_pol; u32 slr_l_transp[2]; u8 res_120[0x200-0x120]; u32 ctrldescl[DCU_LAYER_MAX_NUM][16]; }; static struct fb_info info; static void reset_total_layers(void) { struct dcu_reg *regs = (struct dcu_reg *)CONFIG_SYS_DCU_ADDR; int i; for (i = 0; i < DCU_LAYER_MAX_NUM; i++) { dcu_write32(®s->ctrldescl[i][0], 0); dcu_write32(®s->ctrldescl[i][1], 0); dcu_write32(®s->ctrldescl[i][2], 0); dcu_write32(®s->ctrldescl[i][3], 0); dcu_write32(®s->ctrldescl[i][4], 0); dcu_write32(®s->ctrldescl[i][5], 0); dcu_write32(®s->ctrldescl[i][6], 0); dcu_write32(®s->ctrldescl[i][7], 0); dcu_write32(®s->ctrldescl[i][8], 0); dcu_write32(®s->ctrldescl[i][9], 0); dcu_write32(®s->ctrldescl[i][10], 0); } dcu_write32(®s->update_mode, DCU_UPDATE_MODE_READREG); } static int layer_ctrldesc_init(int index, u32 pixel_format) { struct dcu_reg *regs = (struct dcu_reg *)CONFIG_SYS_DCU_ADDR; unsigned int bpp = BPP_24_RGB888; dcu_write32(®s->ctrldescl[index][0], DCU_CTRLDESCLN_1_HEIGHT(info.var.yres) | DCU_CTRLDESCLN_1_WIDTH(info.var.xres)); dcu_write32(®s->ctrldescl[index][1], DCU_CTRLDESCLN_2_POSY(0) | DCU_CTRLDESCLN_2_POSX(0)); dcu_write32(®s->ctrldescl[index][2], (unsigned int)info.screen_base); switch (pixel_format) { case 16: bpp = BPP_16_RGB565; break; case 24: bpp = BPP_24_RGB888; break; case 32: bpp = BPP_32_ARGB8888; break; default: printf("unsupported color depth: %u\n", pixel_format); } dcu_write32(®s->ctrldescl[index][3], DCU_CTRLDESCLN_4_EN | DCU_CTRLDESCLN_4_TRANS(0xff) | DCU_CTRLDESCLN_4_BPP(bpp) | DCU_CTRLDESCLN_4_AB(0)); dcu_write32(®s->ctrldescl[index][4], DCU_CTRLDESCLN_5_CKMAX_R(0xff) | DCU_CTRLDESCLN_5_CKMAX_G(0xff) | DCU_CTRLDESCLN_5_CKMAX_B(0xff)); dcu_write32(®s->ctrldescl[index][5], DCU_CTRLDESCLN_6_CKMIN_R(0) | DCU_CTRLDESCLN_6_CKMIN_G(0) | DCU_CTRLDESCLN_6_CKMIN_B(0)); dcu_write32(®s->ctrldescl[index][6], DCU_CTRLDESCLN_7_TILE_VER(0) | DCU_CTRLDESCLN_7_TILE_HOR(0)); dcu_write32(®s->ctrldescl[index][7], DCU_CTRLDESCLN_8_FG_FCOLOR(0)); dcu_write32(®s->ctrldescl[index][8], DCU_CTRLDESCLN_9_BG_BCOLOR(0)); dcu_write32(®s->update_mode, DCU_UPDATE_MODE_READREG); return 0; } int fsl_dcu_init(unsigned int xres, unsigned int yres, unsigned int pixel_format) { struct dcu_reg *regs = (struct dcu_reg *)CONFIG_SYS_DCU_ADDR; unsigned int div, mode; /* Memory allocation for framebuffer */ info.screen_size = info.var.xres * info.var.yres * (info.var.bits_per_pixel / 8); info.screen_base = (char *)memalign(ARCH_DMA_MINALIGN, roundup(info.screen_size, ARCH_DMA_MINALIGN)); memset(info.screen_base, 0, info.screen_size); reset_total_layers(); div = dcu_set_pixel_clock(info.var.pixclock); dcu_write32(®s->div_ratio, (div - 1)); dcu_write32(®s->disp_size, DCU_DISP_SIZE_DELTA_Y(info.var.yres) | DCU_DISP_SIZE_DELTA_X(info.var.xres / 16)); dcu_write32(®s->hsyn_para, DCU_HSYN_PARA_BP(info.var.left_margin) | DCU_HSYN_PARA_PW(info.var.hsync_len) | DCU_HSYN_PARA_FP(info.var.right_margin)); dcu_write32(®s->vsyn_para, DCU_VSYN_PARA_BP(info.var.upper_margin) | DCU_VSYN_PARA_PW(info.var.vsync_len) | DCU_VSYN_PARA_FP(info.var.lower_margin)); dcu_write32(®s->synpol, DCU_SYN_POL_INV_PXCK_FALL | DCU_SYN_POL_NEG_REMAIN | DCU_SYN_POL_INV_VS_LOW | DCU_SYN_POL_INV_HS_LOW); dcu_write32(®s->bgnd, DCU_BGND_R(0) | DCU_BGND_G(0) | DCU_BGND_B(0)); dcu_write32(®s->mode, DCU_MODE_BLEND_ITER(DCU_LAYER_MAX_NUM) | DCU_MODE_RASTER_EN); dcu_write32(®s->threshold, DCU_THRESHOLD_LS_BF_VS(0x3) | DCU_THRESHOLD_OUT_BUF_HIGH(0x78) | DCU_THRESHOLD_OUT_BUF_LOW(0)); mode = dcu_read32(®s->mode); dcu_write32(®s->mode, mode | DCU_MODE_NORMAL); layer_ctrldesc_init(0, pixel_format); return 0; } void *video_hw_init(void) { static GraphicDevice ctfb; const char *options; unsigned int depth = 0, freq = 0; struct fb_videomode *fsl_dcu_mode_db = &fsl_dcu_mode_480_272; if (!video_get_video_mode(&ctfb.winSizeX, &ctfb.winSizeY, &depth, &freq, &options)) return NULL; /* Find the monitor port, which is a required option */ if (!options) return NULL; if (strncmp(options, "monitor=", 8) != 0) return NULL; switch (RESOLUTION(ctfb.winSizeX, ctfb.winSizeY)) { case RESOLUTION(480, 272): fsl_dcu_mode_db = &fsl_dcu_mode_480_272; break; case RESOLUTION(640, 480): fsl_dcu_mode_db = &fsl_dcu_mode_640_480; break; default: printf("unsupported resolution %ux%u\n", ctfb.winSizeX, ctfb.winSizeY); } info.var.xres = fsl_dcu_mode_db->xres; info.var.yres = fsl_dcu_mode_db->yres; info.var.bits_per_pixel = 32; info.var.pixclock = fsl_dcu_mode_db->pixclock; info.var.left_margin = fsl_dcu_mode_db->left_margin; info.var.right_margin = fsl_dcu_mode_db->right_margin; info.var.upper_margin = fsl_dcu_mode_db->upper_margin; info.var.lower_margin = fsl_dcu_mode_db->lower_margin; info.var.hsync_len = fsl_dcu_mode_db->hsync_len; info.var.vsync_len = fsl_dcu_mode_db->vsync_len; info.var.sync = fsl_dcu_mode_db->sync; info.var.vmode = fsl_dcu_mode_db->vmode; info.fix.line_length = info.var.xres * info.var.bits_per_pixel / 8; if (platform_dcu_init(ctfb.winSizeX, ctfb.winSizeY, options + 8, fsl_dcu_mode_db) < 0) return NULL; ctfb.frameAdrs = (unsigned int)info.screen_base; ctfb.plnSizeX = ctfb.winSizeX; ctfb.plnSizeY = ctfb.winSizeY; ctfb.gdfBytesPP = 4; ctfb.gdfIndex = GDF_32BIT_X888RGB; ctfb.memSize = info.screen_size; return &ctfb; }