blob: 89c7ad7eedb6aa29c361d3e518699b39f44b49a8 [file] [log] [blame]
/*
* (C) Copyright 2012
* Marvell Semiconductor <www.marvell.com>
* Written-by: Green Wan <gwan@marvell.com>
* Jun Nie <njun@marvell.com>
* Kevin Liu <kliu5@marvell.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <common.h>
#include <malloc.h>
#include <linux/types.h>
#include <asm/io.h>
#include <i2c.h>
#include <asm/gpio.h>
#include <mmp_disp.h>
struct mmp_disp_info *mmp_disp_fbi;
int g_panel_id;
struct lcd_regs *get_regs(struct mmp_disp_info *fbi)
{
struct lcd_regs *regs = (struct lcd_regs *)((uintptr_t)fbi->reg_base);
if (fbi->id == 0)
regs = (struct lcd_regs *)((uintptr_t)fbi->reg_base + 0xc0);
if (fbi->id == 2)
regs = (struct lcd_regs *)((uintptr_t)fbi->reg_base + 0x200);
return regs;
}
u32 dma_ctrl_read(struct mmp_disp_info *fbi, int ctrl1)
{
u32 reg = (uintptr_t)fbi->reg_base + dma_ctrl(ctrl1, fbi->id);
return __raw_readl((uintptr_t)reg);
}
void dma_ctrl_write(struct mmp_disp_info *fbi, int ctrl1, u32 value)
{
u32 reg = (u32)(uintptr_t)fbi->reg_base + dma_ctrl(ctrl1, fbi->id);
__raw_writel(value, (uintptr_t)reg);
}
void dma_ctrl_set(struct mmp_disp_info *fbi, int ctrl1, u32 mask, u32 value)
{
u32 reg = (u32)(uintptr_t)fbi->reg_base + dma_ctrl(ctrl1, fbi->id);
u32 tmp1, tmp2;
tmp1 = __raw_readl((uintptr_t)reg);
tmp2 = tmp1;
tmp2 &= ~mask;
tmp2 |= value;
if (tmp1 != tmp2)
__raw_writel(tmp2, (uintptr_t)reg);
}
static void set_pix_fmt(struct mmp_disp_plat_info *mi, int pix_fmt)
{
switch (pix_fmt) {
case PIX_FMT_RGB565:
mi->bits_per_pixel = 16;
break;
case PIX_FMT_BGR565:
mi->bits_per_pixel = 16;
break;
case PIX_FMT_RGB1555:
mi->bits_per_pixel = 16;
break;
case PIX_FMT_BGR1555:
mi->bits_per_pixel = 16;
break;
case PIX_FMT_RGB888PACK:
mi->bits_per_pixel = 24;
break;
case PIX_FMT_BGR888PACK:
mi->bits_per_pixel = 24;
break;
case PIX_FMT_RGB888UNPACK:
mi->bits_per_pixel = 32;
break;
case PIX_FMT_BGR888UNPACK:
mi->bits_per_pixel = 32;
break;
case PIX_FMT_RGBA888:
mi->bits_per_pixel = 32;
break;
case PIX_FMT_BGRA888:
mi->bits_per_pixel = 32;
break;
}
}
/*
* The hardware clock divider has an integer and a fractional
* stage:
*
* clk2 = clk_in / integer_divider
* clk_out = clk2 * (1 - (fractional_divider >> 12))
*
* Calculate integer and fractional divider for given clk_in
* and clk_out.
*/
static void set_clock_divider(struct mmp_disp_info *fbi)
{
struct mmp_disp_plat_info *mi = fbi->mi;
struct dsi_info *di = (struct dsi_info *)mi->phy_info;
u32 val = mi->sclk_div, reg;
/* for lcd controller */
writel(val & (~0xf00), fbi->reg_base + clk_div(fbi->id));
/* for dsi clock */
if (mi->phy_type & (DSI | DSI2DPI)) {
if (di->id == 1) {
reg = readl(fbi->reg_base + clk_div(0));
reg &= ~0xf00;
reg |= val & 0xf00;
writel(reg, fbi->reg_base + clk_div(0));
} else if (di->id == 2) {
reg = readl(fbi->reg_base + clk_div(1));
reg &= ~0xf00;
reg |= val & 0xf00;
writel(reg, fbi->reg_base + clk_div(1));
writel(val & (~0xf00), fbi->reg_base + clk_div(2));
}
}
}
static u32 dma_ctrl0_update(int active, struct mmp_disp_plat_info *mi,
u32 x, u32 pix_fmt)
{
int tmp, shift1, shift2;
tmp = 0x100;
shift1 = 16;
shift2 = 12;
if (active)
x |= tmp;
else
x &= ~tmp;
/* If we are in a pseudo-color mode, we need to enable
* palette lookup */
if (pix_fmt == PIX_FMT_PSEUDOCOLOR)
x |= 0x10000000;
/* Configure hardware pixel format */
x &= ~(0xF << shift1);
x |= (pix_fmt >> 1) << shift1;
/* Check YUV422PACK */
x &= ~((1 << 9) | (1 << 11) | (1 << 10) | (1 << 12));
if (((pix_fmt >> 1) == 5) || (pix_fmt & 0x1000)) {
x |= 1 << 9;
x |= (mi->panel_rbswap) << 12;
if (pix_fmt == 11)
x |= 1 << 11;
if (pix_fmt & 0x1000)
x |= 1 << 10;
} else {
/* Check red and blue pixel swap.
* 1. source data swap. BGR[M:L] rather than RGB[M:L] is
* stored in memeory as source format.
* 2. panel output data swap
*/
x |= (((pix_fmt & 1) ^ 1) ^ (mi->panel_rbswap)) << shift2;
}
/* enable horizontal smooth filter for both graphic and video layers */
x |= CFG_GRA_HSMOOTH(1) | CFG_DMA_HSMOOTH(1);
return x;
}
static void set_dma_control0(struct mmp_disp_info *fbi)
{
struct mmp_disp_plat_info *mi;
u32 x = 0, active, pix_fmt = fbi->pix_fmt;
/* Set bit to enable graphics DMA */
dma_ctrl_set(fbi, 0, CFG_ARBFAST_ENA(1), CFG_ARBFAST_ENA(1));
mi = fbi->mi;
active = fbi->active;
x = dma_ctrl_read(fbi, 0);
active = 1;
x = dma_ctrl0_update(active, mi, x, pix_fmt);
dma_ctrl_write(fbi, 0, x);
/* enable multiple burst request in DMA AXI bus arbiter for
* faster read
*/
x = readl(fbi->reg_base + LCD_SPU_DMA_CTRL0);
x |= CFG_ARBFAST_ENA(1);
writel(x, fbi->reg_base + LCD_SPU_DMA_CTRL0);
}
static void set_graphics_start(struct mmp_disp_info *fb, int xoffset,
int yoffset)
{
struct mmp_disp_info *fbi = fb;
int pixel_offset;
unsigned int phys_addr;
static int debugcount;
#ifdef CONFIG_VDMA
struct mmp_vdma_reg *vdma_reg = (struct mmp_vdma_reg *)fbi->vdma_reg_base;
struct mmp_vdma_ch_reg *ch_reg = &vdma_reg->ch0_reg;
#else
struct lcd_regs *regs = get_regs(fbi);
#endif
if (debugcount < 10)
debugcount++;
pixel_offset = (yoffset * ALIGN(fbi->mi->modes->xres, 16)) + xoffset;
phys_addr = (unsigned int)fbi->fb_start +
(pixel_offset * (fbi->mi->bits_per_pixel >> 3));
#ifdef CONFIG_VDMA
/* Set VDMA Source address */
writel(phys_addr, &ch_reg->src_addr);
#else
writel(phys_addr, &regs->g_0);
#endif
}
static void set_screen(struct mmp_disp_info *fbi, struct mmp_disp_plat_info *mi)
{
struct lcd_regs *regs = get_regs(fbi);
struct dsi_info *di = NULL;
u32 x, h_porch, vsync_ctrl, vec = 10;
u32 xres = mi->modes->xres, yres = mi->modes->yres;
u32 xres_z = mi->modes->xres, yres_z = mi->modes->yres;
u32 bits_per_pixel = mi->bits_per_pixel;
if (mi->phy_type & (DSI2DPI | DSI)) {
di = (struct dsi_info *)mi->phy_info;
vec = ((di->lanes <= 2) ? 1 : 2) * 10 * di->bpp / 8 / di->lanes;
}
/* resolution, active */
writel((mi->modes->yres << 16) | mi->modes->xres, &regs->screen_active);
/* pitch, pixels per line */
x = readl(&regs->g_pitch);
x = (x & ~0xFFFF) | ((ALIGN(xres, 16) * bits_per_pixel) >> 3);
writel(x, &regs->g_pitch);
/* resolution, src size */
writel((yres << 16) | xres, &regs->g_size);
/* resolution, dst size */
if (DISP_GEN4(fbi->mi->version)) {
if (xres != xres_z || yres != yres_z)
printf("resize not support for graphic layer of GEN4\n");
} else {
writel((yres_z << 16) | xres_z, &regs->g_size_z);
}
/* h porch, left/right margin */
if (mi->phy_type & (DSI2DPI | DSI)) {
h_porch = (mi->modes->xres + mi->modes->right_margin) * vec / 10
- mi->modes->xres;
h_porch = (mi->modes->left_margin * vec / 10) << 16 | h_porch;
} else {
h_porch = (mi->modes->left_margin) << 16 | mi->modes->right_margin;
}
writel(h_porch, &regs->screen_h_porch);
/* v porch, upper/lower margin */
writel((mi->modes->upper_margin << 16) | mi->modes->lower_margin,
&regs->screen_v_porch);
/* vsync ctrl */
if (mi->phy_type & (DSI2DPI | DSI)) {
vsync_ctrl = (((mi->modes->xres + mi->modes->right_margin) * vec / 10) << 16)
| ((mi->modes->xres + mi->modes->right_margin) * vec / 10);
} else {
vsync_ctrl = ((mi->modes->xres + mi->modes->right_margin) << 16)
| (mi->modes->xres + mi->modes->right_margin);
}
writel(vsync_ctrl, &regs->vsync_ctrl); /* FIXME */
/* blank color */
writel(0x00000000, &regs->blank_color);
}
static void set_dumb_panel_control(struct mmp_disp_info *fbi,
struct mmp_disp_plat_info *mi)
{
u32 x;
x = readl(fbi->reg_base + intf_ctrl(fbi->id)) & 0x00000001;
x |= (fbi->is_blanked ? 0x7 : mi->dumb_mode) << 28;
x |= (mi->modes->sync & 2) ? 0 : 0x00000008;
x |= (mi->modes->sync & 1) ? 0 : 0x00000004;
x |= (mi->modes->sync & 8) ? 0 : 0x00000020;
x |= mi->gpio_output_data << 20;
x |= mi->gpio_output_mask << 12;
x |= mi->panel_rgb_reverse_lanes ? 0x00000080 : 0;
x |= mi->invert_composite_blank ? 0x00000040 : 0;
x |= mi->invert_pix_val_ena ? 0x00000010 : 0;
x |= mi->invert_pixclock ? 0x00000002 : 0;
writel(x, fbi->reg_base + intf_ctrl(fbi->id)); /* FIXME */
}
static void set_dumb_screen_dimensions(struct mmp_disp_info *fbi,
struct mmp_disp_plat_info *mi)
{
struct lcd_regs *regs = get_regs(fbi);
struct dsi_info *di = NULL;
int x;
int y;
int vec = 10;
/* FIXME - need to double check (*3) and (*2) */
if (mi->phy_type & (DSI2DPI | DSI)) {
di = (struct dsi_info *)mi->phy_info;
vec = ((di->lanes <= 2) ? 1 : 2) * 10 * di->bpp / 8 / di->lanes;
if (di->master_mode) {
if (DISP_GEN4(fbi->mi->version))
writel(timing_master_config_dc4(fbi->id,
di->id - 1, di->id - 1),
fbi->reg_base + TIMING_MASTER_CONTROL_DC4);
else
writel(timing_master_config(fbi->id,
di->id - 1, di->id - 1),
fbi->reg_base + TIMING_MASTER_CONTROL);
}
}
x = mi->modes->xres + mi->modes->right_margin +
mi->modes->hsync_len + mi->modes->left_margin;
x = x * vec / 10;
y = mi->modes->yres + mi->modes->lower_margin +
mi->modes->vsync_len + mi->modes->upper_margin;
writel((y << 16) | x, &regs->screen_size);
}
static int mmp_disp_set_par(struct mmp_disp_info *fb,
struct mmp_disp_plat_info *mi)
{
struct mmp_disp_info *fbi = fb;
int pix_fmt;
u32 x;
/* Determine which pixel format we're going to use */
pix_fmt = mi->pix_fmt; /* choose one */
if (pix_fmt < 0)
return pix_fmt;
fbi->pix_fmt = pix_fmt;
set_pix_fmt(mi, pix_fmt);
/* Calculate clock divisor. */
set_clock_divider(fbi);
/* Configure graphics DMA parameters.
* Configure global panel parameters.
*/
set_screen(fbi, mi);
/* Configure dumb panel ctrl regs & timings */
set_dumb_panel_control(fbi, mi);
set_dumb_screen_dimensions(fbi, mi);
x = readl(fbi->reg_base + intf_ctrl(fbi->id));
if ((x & 1) == 0)
writel(x | 1, fbi->reg_base + intf_ctrl(fbi->id));
set_graphics_start(fbi, 0, 0);
set_dma_control0(fbi);
return 0;
}
static void mmp_disp_set_default(struct mmp_disp_info *fbi,
struct mmp_disp_plat_info *mi)
{
struct lcd_regs *regs = get_regs(fbi);
u32 dma_ctrl1 = 0x20320081;
u32 burst_length = (mi->burst_len == 16) ?
CFG_CYC_BURST_LEN16 : CFG_CYC_BURST_LEN8;
/* Configure default register values */
writel(mi->io_pin_allocation_mode | burst_length,
fbi->reg_base + SPU_IOPAD_CONTROL);
/* enable 16 cycle burst length to get better formance */
writel(0x00000000, &regs->blank_color);
writel(0x00000000, &regs->g_1);
writel(0x00000000, &regs->g_start);
/*
* vsync in LCD internal controller is always positive,
* we default configure dma trigger @vsync falling edge,
* so that DMA idle time between DMA frame done and
* next DMA transfer begin can be as large as possible
*/
dma_ctrl1 |= CFG_VSYNC_INV_MASK;
dma_ctrl_write(fbi, 1, dma_ctrl1);
}
static void rect_fill(void *addr, int left, int up, int right,
int down, int width, unsigned int color, int pix_fmt)
{
int i, j;
for (j = up; j < down; j++)
for (i = left; i < right; i++)
if (pix_fmt == PIX_FMT_RGB565)
*((unsigned short *)addr + j * width + i) = color;
else if (pix_fmt == PIX_FMT_RGBA888)
*((unsigned int *)addr + j * width + i) = color;
}
void test_panel(int xres, int yres, int pix_fmt)
{
int w = xres, h = yres, bpp = 2;
int x = w / 8, y = h / 8;
int color_blue = 0x1f, color_green = 0x7e0, color_red = 0xf800;
printf("panel test: white background, test RGB color\r\n");
if (pix_fmt == PIX_FMT_RGB565) {
color_blue = 0x1f;
color_green = 0x7e0;
color_red = 0xf800;
bpp = 2;
} else if (pix_fmt == PIX_FMT_RGBA888) {
color_blue = 0xff0000ff;
color_green = 0xff00ff00;
color_red = 0xffff0000;
bpp = 4;
}
memset((void *)DEFAULT_FB_BASE, 0xff,
w * h * bpp);
udelay(50 * 1000);
rect_fill((void *)DEFAULT_FB_BASE, x, y, w - x,
h - y, w, color_blue, pix_fmt);
udelay(50 * 1000);
rect_fill((void *)DEFAULT_FB_BASE, x * 2, y * 2,
w - x * 2, h - y * 2, w, color_green, pix_fmt);
udelay(50 * 1000);
rect_fill((void *)DEFAULT_FB_BASE, x * 3, y * 3,
w - x * 3, h - y * 3, w, color_red, pix_fmt);
udelay(50 * 1000);
flush_cache(DEFAULT_FB_BASE, DEFAULT_FB_SIZE);
}
#ifdef CONFIG_VDMA
static void vdma_enable(struct mmp_disp_info *fbi)
{
struct lcd_regs *regs = get_regs(fbi);
struct mmp_vdma_reg *vdma_reg =
(struct mmp_vdma_reg *)(fbi->vdma_reg_base);
struct mmp_vdma_ch_reg *ch_reg = &vdma_reg->ch0_reg;
u32 pitch, size, src_sz, height;
u32 tmp, tmp1, tmp2, mask, set;
/*
* Select VDMA Channel 0: VMDA1
* LCD_VDMA_SEL_CTRL bit21:20->0 : PN graphic path use VMDA1
* LCD_VDMA_SEL_CTRL bit2:0->0 : VDMA1 map to PN graphic path
*/
tmp = readl(fbi->reg_base + LCD_VDMA_SEL_CTRL);
tmp &= ~((0x3 << 20) | 0x7);
writel(tmp, fbi->reg_base + LCD_VDMA_SEL_CTRL);
if (!DISP_GEN4(fbi->mi->version)) {
/* Select PN Graphic SQU Line Buffer @0x2ac bit27:26 ->0 */
tmp = readl(fbi->reg_base + LCD_SCALING_SQU_CTL_INDEX);
tmp &= ~(0x3 << 26);
writel(tmp, fbi->reg_base + LCD_SCALING_SQU_CTL_INDEX);
}
pitch = ((ALIGN(fbi->mi->modes->xres, 16)
* fbi->mi->bits_per_pixel) >> 3) & 0xffff;
/*
* Set source address in SQU of LCD Controller
* and destination address in SQU of VDMA
*/
if (DISP_GEN4(fbi->mi->version)) {
writel(0x00000000, &regs->g_squln);
writel(0x00000000, &ch_reg->dst_addr);
} else {
if (pitch * VDMA_SRAM_LINES > 64*1024) {
printf("error: requested vdma size exceed 64KB!\n");
hang();
}
/* The sram bank size is 64k and use the tail for vdma */
writel(CONFIG_SRAM_BASE + 64 * 1024 - pitch * VDMA_SRAM_LINES,
fbi->reg_base + LCD_PN_SQULN_CTRL);
writel(CONFIG_SRAM_BASE + 64 * 1024 - pitch * VDMA_SRAM_LINES,
&ch_reg->dst_addr);
}
/* Set and Enable Panel Graphic Path SQU Line Buffers */
tmp2 = readl(fbi->reg_base + LCD_PN_SQULN_CTRL);
tmp1 = tmp2;
tmp1 &= ~0x3f;
tmp1 |= ((VDMA_SRAM_LINES / 2 - 1) << 1) | 0x1;
if (tmp1 != tmp2)
writel(tmp1, fbi->reg_base + LCD_PN_SQULN_CTRL);
height = fbi->mi->modes->yres & 0xffff;
src_sz = pitch * height;
size = height << 16 | pitch;
/* FIXME: set dst and src with the same pitch */
pitch |= pitch << 16;
writel(pitch, &ch_reg->pitch);
writel(src_sz, &ch_reg->src_size);
writel(size, &ch_reg->dst_size);
tmp = readl(&ch_reg->ctrl) & (~(0xff << 8));
tmp |= VDMA_SRAM_LINES << 8;
writel(tmp, &ch_reg->ctrl);
/* Enable VDMA Channel 0 */
if (DISP_GEN4(fbi->mi->version)) {
mask = (0x1f << 20) | 0x3;
set = (0x10 << 20) | 0x1;
/* FIXME: bypass decompression */
tmp = readl((uintptr_t)&vdma_reg->dec_ctrl) | (1 << 31);
writel(tmp, &vdma_reg->dec_ctrl);
} else {
mask = (0x3 << 4) | (0x3 << 6) | 0x3;
set = (0x2 << 4) | (0x2 << 6) | 0x1;
}
tmp2 = readl(&ch_reg->ctrl);
tmp1 = tmp2;
tmp1 &= ~mask;
tmp1 |= set;
if (tmp1 != tmp2)
writel(tmp1, &ch_reg->ctrl);
/*Load All VDMA Path 0 Configure Registers */
tmp = readl(&vdma_reg->main_ctrl) | (1 << 24);
writel(tmp, &vdma_reg->main_ctrl);
}
#endif
void *mmp_disp_init(struct mmp_disp_plat_info *mi)
{
struct mmp_disp_info *fbi = malloc(sizeof(struct mmp_disp_info));
struct dsi_info *di = NULL;
#if defined(DEBUG_MMP_DISP)
u32 i;
#endif
int ret = 0;
memset(fbi, 0, sizeof(struct mmp_disp_info));
/* Initialize private data */
fbi->panel_rbswap = mi->panel_rbswap;
fbi->id = mi->index;
mmp_disp_fbi = fbi;
fbi->mi = mi;
fbi->is_blanked = 0;
fbi->debug = 0;
fbi->active = mi->active;
/* Map LCD controller registers */
fbi->reg_base = (void *)DISPLAY_CONTROLLER_BASE;
#ifdef CONFIG_VDMA
/* Map VDMA Controller Registers */
fbi->vdma_reg_base = (void *)VDMA_CONTROLLER_BASE;
#endif
if (mi->phy_type & (DSI2DPI | DSI)) {
di = (struct dsi_info *)mi->phy_info;
di->regs = DSI1_REG_BASE; /* DSI 1 */
}
/*
* Allocate framebuffer memory
*/
fbi->fb_size = DEFAULT_FB_SIZE;
fbi->fb_start = (unsigned int *)DEFAULT_FB_BASE;
memset(fbi->fb_start, 0x0, fbi->fb_size);
/* LCD_TOP_CTRL control reg */
writel(VDMA_ENABLE, fbi->reg_base + LCD_TOP_CTRL);
/* Get DISP Subsystem Verison */
fbi->mi->version = readl(fbi->reg_base + LCD_VERSION);
/*
* Fill in sane defaults
*/
mmp_disp_set_default(fbi, mi); /* FIXME */
mmp_disp_set_par(fbi, mi);
#ifdef CONFIG_VDMA
vdma_enable(fbi);
#endif
/* Load All Configure Registers with Shadow Registers */
if (DISP_GEN4(fbi->mi->version))
writel(1 << 30, fbi->reg_base + LCD_SHADOW_CTRL);
if (mi->phy_type & (DSI2DPI | DSI)) {
/* phy interface init */
ret = mmp_disp_dsi_init(fbi);
if (ret)
goto err;
}
/* dump all lcd and dsi registers for debug purpose */
#if defined(DEBUG_MMP_DISP)
printf("lcd regs:\n");
for (i = 0; i < 0x300; i += 4) {
if (!(i % 16) && i)
printf("\n0x%3x: ", i);
printf(" %8x", readl(fbi->reg_base + i));
}
if (mi->phy_type & (DSI2DPI | DSI)) {
printf("\n dsi regs:");
for (i = 0x0; i < 0x200; i += 4) {
if (!(i % 16))
printf("\n0x%3x: ", i);
printf(" %8x", readl(di->regs + i));
}
}
printf("\n");
test_panel(fbi->mi->modes->xres, fbi->mi->modes->yres, fbi->pix_fmt);
#endif
return (void *)fbi;
err:
free(fbi);
return NULL;
}