From 4460ae68a11b629dd75b9f7247f8a4c3c29fc97e Mon Sep 17 00:00:00 2001 From: yxj Date: Tue, 15 Jan 2013 20:02:21 +0800 Subject: [PATCH] rk3188:add lcdc support --- drivers/video/rockchip/Kconfig | 17 + drivers/video/rockchip/Makefile | 3 +- drivers/video/rockchip/lcdc/rk3188_lcdc.c | 932 ++++++++++++++++++++++ drivers/video/rockchip/lcdc/rk3188_lcdc.h | 371 +++++++++ include/linux/rk_fb.h | 40 +- include/linux/rk_screen.h | 4 +- 6 files changed, 1355 insertions(+), 12 deletions(-) create mode 100644 drivers/video/rockchip/lcdc/rk3188_lcdc.c create mode 100644 drivers/video/rockchip/lcdc/rk3188_lcdc.h diff --git a/drivers/video/rockchip/Kconfig b/drivers/video/rockchip/Kconfig index a70c22257ee6..cc05d0aded2f 100755 --- a/drivers/video/rockchip/Kconfig +++ b/drivers/video/rockchip/Kconfig @@ -103,6 +103,23 @@ config LCDC1_RK3066B help Support lcdc1 if you say y here +config LCDC_RK3188 + bool "rk3188 lcdc support" + depends on FB_ROCKCHIP && ARCH_RK3188 + help + Driver for rk3188 lcdc.There are two lcd controllers on rk3188 +config LCDC0_RK3188 + bool "lcdc0 support" + depends on LCDC_RK3188 + help + Support rk3188 lcdc0 if you say y here + +config LCDC1_RK3188 + bool "lcdc1 support" + depends on LCDC_RK3188 + help + Support rk3188 lcdc1 if you say y here + source "drivers/video/rockchip/hdmi/Kconfig" source "drivers/video/rockchip/rga/Kconfig" source "drivers/video/rockchip/lvds/Kconfig" diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index 0a2b769e0736..93ed3edc7ab9 100755 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -1,7 +1,8 @@ obj-$(CONFIG_FB_ROCKCHIP) += rk_fb.o rkfb_sysfs.o obj-$(CONFIG_LCDC_RK30) += lcdc/rk30_lcdc.o obj-$(CONFIG_LCDC_RK2928) += lcdc/rk2928_lcdc.o -obj-$(CONFIG_LCDC_RK3066B) += lcdc/rk3066b_lcdc.o +obj-$(CONFIG_LCDC_RK3066B) += lcdc/rk3066b_lcdc.o +obj-$(CONFIG_LCDC_RK3188) += lcdc/rk3188_lcdc.o obj-$(CONFIG_RGA_RK30) += rga/ obj-$(CONFIG_RK_HDMI) += hdmi/ obj-$(CONFIG_RK_LVDS) += lvds/ diff --git a/drivers/video/rockchip/lcdc/rk3188_lcdc.c b/drivers/video/rockchip/lcdc/rk3188_lcdc.c new file mode 100644 index 000000000000..5385573bfcf4 --- /dev/null +++ b/drivers/video/rockchip/lcdc/rk3188_lcdc.c @@ -0,0 +1,932 @@ +/* + * drivers/video/rockchip/lcdc/rk3188_lcdc.c + * + * Copyright (C) 2013 ROCKCHIP, Inc. + *Author:yxj + *This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rk3188_lcdc.h" + + + +static int dbg_thresd = 0; +module_param(dbg_thresd, int, S_IRUGO|S_IWUSR); +#define DBG(level,x...) do { \ + if(unlikely(dbg_thresd >= level)) \ + printk(KERN_INFO x);} while (0) + +//#define WAIT_FOR_SYNC 1 +static int rk3188_lcdc_clk_enable(struct rk3188_lcdc_device *lcdc_dev) +{ + + clk_enable(lcdc_dev->pd); + clk_enable(lcdc_dev->hclk); + clk_enable(lcdc_dev->dclk); + clk_enable(lcdc_dev->aclk); + + spin_lock(&lcdc_dev->reg_lock); + lcdc_dev->clk_on = 1; + spin_unlock(&lcdc_dev->reg_lock); + printk("rk3188 lcdc%d clk enable...\n",lcdc_dev->id); + + return 0; +} + +static int rk3188_lcdc_clk_disable(struct rk3188_lcdc_device *lcdc_dev) +{ + spin_lock(&lcdc_dev->reg_lock); + lcdc_dev->clk_on = 0; + spin_unlock(&lcdc_dev->reg_lock); + + clk_disable(lcdc_dev->dclk); + clk_disable(lcdc_dev->hclk); + clk_disable(lcdc_dev->aclk); + clk_disable(lcdc_dev->pd); + printk("rk3188 lcdc%d clk disable...\n",lcdc_dev->id); + + return 0; +} + +static int rk3188_lcdc_resume_reg(struct rk3188_lcdc_device *lcdc_dev) +{ + memcpy((u8*)lcdc_dev->regs, (u8*)lcdc_dev->regsbak, 0x84); + return 0; +} + + +//enable layer,open:1,enable;0 disable +static int win0_open(struct rk3188_lcdc_device *lcdc_dev,bool open) +{ + spin_lock(&lcdc_dev->reg_lock); + if(likely(lcdc_dev->clk_on)) + { + if(open) + { + if(!lcdc_dev->atv_layer_cnt) + { + printk(KERN_INFO "lcdc%d wakeup from standby!\n",lcdc_dev->id); + lcdc_msk_reg(lcdc_dev, SYS_CTRL,m_LCDC_STANDBY,v_LCDC_STANDBY(0)); + } + lcdc_dev->atv_layer_cnt++; + } + else if((lcdc_dev->atv_layer_cnt > 0) && (!open)) + { + lcdc_dev->atv_layer_cnt--; + } + lcdc_dev->driver.layer_par[0]->state = open; + + lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_WIN0_EN, v_WIN0_EN(open)); + if(!lcdc_dev->atv_layer_cnt) //if no layer used,disable lcdc + { + printk(KERN_INFO "no layer of lcdc%d is used,go to standby!\n",lcdc_dev->id); + lcdc_msk_reg(lcdc_dev, SYS_CTRL,m_LCDC_STANDBY,v_LCDC_STANDBY(1)); + } + lcdc_cfg_done(lcdc_dev); + } + spin_unlock(&lcdc_dev->reg_lock); + + + return 0; +} + +static int win1_open(struct rk3188_lcdc_device *lcdc_dev,bool open) +{ + spin_lock(&lcdc_dev->reg_lock); + if(likely(lcdc_dev->clk_on)) + { + if(open) + { + if(!lcdc_dev->atv_layer_cnt) + { + printk(KERN_INFO "lcdc%d wakeup from standby!\n",lcdc_dev->id); + lcdc_msk_reg(lcdc_dev, SYS_CTRL,m_LCDC_STANDBY,v_LCDC_STANDBY(0)); + } + lcdc_dev->atv_layer_cnt++; + } + else if((lcdc_dev->atv_layer_cnt > 0) && (!open)) + { + lcdc_dev->atv_layer_cnt--; + } + lcdc_dev->driver.layer_par[1]->state = open; + + lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_WIN1_EN, v_WIN1_EN(open)); + if(!lcdc_dev->atv_layer_cnt) //if no layer used,disable lcdc + { + printk(KERN_INFO "no layer of lcdc%d is used,go to standby!\n",lcdc_dev->id); + lcdc_msk_reg(lcdc_dev, SYS_CTRL,m_LCDC_STANDBY,v_LCDC_STANDBY(1)); + } + lcdc_cfg_done(lcdc_dev); + } + spin_unlock(&lcdc_dev->reg_lock); + + return 0; +} + +static int rk3188_lcdc_open(struct rk_lcdc_device_driver *dev_drv,int layer_id,bool open) +{ + int i=0; + int __iomem *c; + int v; + struct rk3188_lcdc_device *lcdc_dev = + container_of(dev_drv,struct rk3188_lcdc_device,driver); + + if((open) && (!lcdc_dev->atv_layer_cnt)) //enable clk,when first layer open + { + rk3188_lcdc_clk_enable(lcdc_dev); + rk3188_lcdc_resume_reg(lcdc_dev); //resume reg + spin_lock(&lcdc_dev->reg_lock); + if(dev_drv->cur_screen->dsp_lut) //resume dsp lut + { + lcdc_msk_reg(lcdc_dev,SYS_CTRL,m_DSP_LUT_EN,v_DSP_LUT_EN(0)); + lcdc_cfg_done(lcdc_dev); + mdelay(25); //wait for dsp lut disabled + for(i=0;i<256;i++) + { + v = dev_drv->cur_screen->dsp_lut[i]; + c = lcdc_dev->dsp_lut_addr_base+i; + writel_relaxed(v,c); + + } + lcdc_msk_reg(lcdc_dev,SYS_CTRL,m_DSP_LUT_EN,v_DSP_LUT_EN(1)); //enable dsp lut + } + spin_unlock(&lcdc_dev->reg_lock); + } + + if(layer_id == 0) + { + win0_open(lcdc_dev,open); + } + else if(layer_id == 1) + { + win1_open(lcdc_dev,open); + } + else + { + printk("invalid win number:%d\n",layer_id); + } + + if((!open) && (!lcdc_dev->atv_layer_cnt)) //when all layer closed,disable clk + { + rk3188_lcdc_clk_disable(lcdc_dev); + } + + printk(KERN_INFO "lcdc%d win%d %s,atv layer:%d\n", + lcdc_dev->id,layer_id,open?"open":"closed", + lcdc_dev->atv_layer_cnt); + return 0; +} + +static int rk3188_lcdc_init(struct rk_lcdc_device_driver *dev_drv) +{ + int i = 0; + int __iomem *c; + int v; + struct rk3188_lcdc_device *lcdc_dev = container_of(dev_drv,struct rk3188_lcdc_device,driver); + if(lcdc_dev->id == 0) //lcdc0 + { + lcdc_dev->pd = clk_get(NULL,"pd_lcdc0"); + lcdc_dev->hclk = clk_get(NULL,"hclk_lcdc0"); + lcdc_dev->aclk = clk_get(NULL,"aclk_lcdc0"); + lcdc_dev->dclk = clk_get(NULL,"dclk_lcdc0"); + } + else if(lcdc_dev->id == 1) + { + lcdc_dev->pd = clk_get(NULL,"pd_lcdc1"); + lcdc_dev->hclk = clk_get(NULL,"hclk_lcdc1"); + lcdc_dev->aclk = clk_get(NULL,"aclk_lcdc1"); + lcdc_dev->dclk = clk_get(NULL,"dclk_lcdc1"); + } + else + { + printk(KERN_ERR "invalid lcdc device!\n"); + return -EINVAL; + } + if (IS_ERR(lcdc_dev->pd) || (IS_ERR(lcdc_dev->aclk)) ||(IS_ERR(lcdc_dev->dclk)) || (IS_ERR(lcdc_dev->hclk))) + { + printk(KERN_ERR "failed to get lcdc%d clk source\n",lcdc_dev->id); + } + + rk3188_lcdc_clk_enable(lcdc_dev); + + lcdc_set_bit(lcdc_dev,SYS_CTRL,m_AUTO_GATING_EN);//eanble axi-clk auto gating for low power + if(dev_drv->cur_screen->dsp_lut) + { + lcdc_msk_reg(lcdc_dev,SYS_CTRL,m_DSP_LUT_EN,v_DSP_LUT_EN(0)); + lcdc_cfg_done(lcdc_dev); + msleep(25); + for(i=0;i<256;i++) + { + v = dev_drv->cur_screen->dsp_lut[i]; + c = lcdc_dev->dsp_lut_addr_base+i; + writel_relaxed(v,c); + + } + lcdc_msk_reg(lcdc_dev,SYS_CTRL,m_DSP_LUT_EN,v_DSP_LUT_EN(1)); + } + + lcdc_cfg_done(lcdc_dev); // write any value to REG_CFG_DONE let config become effective + + rk3188_lcdc_clk_disable(lcdc_dev); + + return 0; +} + + +//set lcdc according the screen info +static int rk3188_load_screen(struct rk_lcdc_device_driver *dev_drv, bool initscreen) +{ + int ret = -EINVAL; + int fps; + u16 face = 0; + struct rk3188_lcdc_device *lcdc_dev = + container_of(dev_drv,struct rk3188_lcdc_device,driver); + rk_screen *screen = dev_drv->cur_screen; + u16 right_margin = screen->right_margin; + u16 left_margin = screen->left_margin; + u16 lower_margin = screen->lower_margin; + u16 upper_margin = screen->upper_margin; + u16 x_res = screen->x_res; + u16 y_res = screen->y_res; + + + spin_lock(&lcdc_dev->reg_lock); + if(likely(lcdc_dev->clk_on)) + { + if(screen->type==SCREEN_MCU) + { + printk("MUC¡¡screen not supported now!\n"); + return -EINVAL; + } + + switch (screen->face) + { + case OUT_P565: + face = OUT_P565; //dither down to rgb565 + lcdc_msk_reg(lcdc_dev, DSP_CTRL0, m_DITHER_DOWN_EN | m_DITHER_DOWN_MODE, v_DITHER_DOWN_EN(1) | v_DITHER_DOWN_MODE(0)); + break; + case OUT_P666: + face = OUT_P666; //dither down to rgb666 + lcdc_msk_reg(lcdc_dev, DSP_CTRL0, m_DITHER_DOWN_EN | m_DITHER_DOWN_MODE, v_DITHER_DOWN_EN(1) | v_DITHER_DOWN_MODE(1)); + break; + case OUT_D888_P565: + face = OUT_P888; + lcdc_msk_reg(lcdc_dev, DSP_CTRL0, m_DITHER_DOWN_EN | m_DITHER_DOWN_MODE, v_DITHER_DOWN_EN(1) | v_DITHER_DOWN_MODE(0)); + break; + case OUT_D888_P666: + face = OUT_P888; + lcdc_msk_reg(lcdc_dev, DSP_CTRL0, m_DITHER_DOWN_EN | m_DITHER_DOWN_MODE, v_DITHER_DOWN_EN(1) | v_DITHER_DOWN_MODE(1)); + break; + case OUT_P888: + face = OUT_P888; + break; + default: + printk("unsupported display output interface!\n"); + break; + } + + //use default overlay,set vsyn hsync den dclk polarity + lcdc_msk_reg(lcdc_dev, DSP_CTRL0,m_DSP_OUT_FORMAT | m_HSYNC_POL | m_VSYNC_POL | + m_DEN_POL |m_DCLK_POL,v_DSP_OUT_FORMAT(face) | v_HSYNC_POL(screen->pin_hsync) | + v_VSYNC_POL(screen->pin_vsync) | v_DEN_POL(screen->pin_den) | v_DCLK_POL(screen->pin_dclk)); + + + //set background color to black,set swap according to the screen panel,disable blank mode + lcdc_msk_reg(lcdc_dev, DSP_CTRL1, m_BG_COLOR| m_DSP_BG_SWAP | m_DSP_RB_SWAP | + m_DSP_RG_SWAP | m_DSP_DELTA_SWAP | m_DSP_DUMMY_SWAP | m_BLANK_EN, + v_BG_COLOR(0x000000) | v_DSP_BG_SWAP(screen->swap_bg) | + v_DSP_RB_SWAP(screen->swap_rb) | v_DSP_RG_SWAP(screen->swap_rg) | + v_DSP_DELTA_SWAP(screen->swap_delta) | v_DSP_DUMMY_SWAP(screen->swap_dumy) | + v_BLANK_EN(0) | v_BLACK_EN(0)); + lcdc_writel(lcdc_dev,DSP_HTOTAL_HS_END,v_HSYNC(screen->hsync_len) | + v_HORPRD(screen->hsync_len + left_margin + x_res + right_margin)); + lcdc_writel(lcdc_dev,DSP_HACT_ST_END,v_HAEP(screen->hsync_len + left_margin + x_res) | + v_HASP(screen->hsync_len + left_margin)); + + lcdc_writel(lcdc_dev,DSP_VTOTAL_VS_END, v_VSYNC(screen->vsync_len) | + v_VERPRD(screen->vsync_len + upper_margin + y_res + lower_margin)); + lcdc_writel(lcdc_dev,DSP_VACT_ST_END,v_VAEP(screen->vsync_len + upper_margin+y_res)| + v_VASP(screen->vsync_len + screen->upper_margin)); + } + spin_unlock(&lcdc_dev->reg_lock); + + ret = clk_set_rate(lcdc_dev->dclk, screen->pixclock); + if(ret) + { + dev_err(dev_drv->dev,"set lcdc%d dclk failed\n",lcdc_dev->id); + } + lcdc_dev->driver.pixclock = lcdc_dev->pixclock = div_u64(1000000000000llu, clk_get_rate(lcdc_dev->dclk)); + + fps = rk_fb_calc_fps(screen,lcdc_dev->pixclock); + screen->ft = 1000/fps; + printk("%s: dclk:%lu>>fps:%d ",lcdc_dev->driver.name,clk_get_rate(lcdc_dev->dclk),fps); + + if(screen->init) + { + screen->init(); + } + + dev_info(dev_drv->dev,"%s for lcdc%d ok!\n",__func__,lcdc_dev->id); + return 0; +} + + +static int win0_set_par(struct rk3188_lcdc_device *lcdc_dev,rk_screen *screen, + struct layer_par *par ) +{ + u32 xact, yact, xvir, yvir, xpos, ypos; + u32 ScaleYrgbX = 0x1000; + u32 ScaleYrgbY = 0x1000; + u32 ScaleCbrX = 0x1000; + u32 ScaleCbrY = 0x1000; + + xact = par->xact; //active (origin) picture window width/height + yact = par->yact; + xvir = par->xvir; // virtual resolution + yvir = par->yvir; + xpos = par->xpos+screen->left_margin + screen->hsync_len; + ypos = par->ypos+screen->upper_margin + screen->vsync_len; + + + ScaleYrgbX = CalScale(xact, par->xsize); //both RGB and yuv need this two factor + ScaleYrgbY = CalScale(yact, par->ysize); + switch (par->format) + { + case YUV422:// yuv422 + ScaleCbrX = CalScale((xact/2), par->xsize); + ScaleCbrY = CalScale(yact, par->ysize); + break; + case YUV420: // yuv420 + ScaleCbrX = CalScale(xact/2, par->xsize); + ScaleCbrY = CalScale(yact/2, par->ysize); + break; + case YUV444:// yuv444 + ScaleCbrX = CalScale(xact, par->xsize); + ScaleCbrY = CalScale(yact, par->ysize); + break; + default: + break; + } + + DBG(1,"%s for lcdc%d>>format:%d>>>xact:%d>>yact:%d>>xsize:%d>>ysize:%d>>xvir:%d>>yvir:%d>>xpos:%d>>ypos:%d>>\n", + __func__,lcdc_dev->id,par->format,xact,yact,par->xsize,par->ysize,xvir,yvir,xpos,ypos); + + spin_lock(&lcdc_dev->reg_lock); + if(likely(lcdc_dev->clk_on)) + { + lcdc_writel(lcdc_dev,WIN0_SCL_FACTOR_YRGB,v_X_SCL_FACTOR(ScaleYrgbX) | v_Y_SCL_FACTOR(ScaleYrgbY)); + lcdc_writel(lcdc_dev,WIN0_SCL_FACTOR_CBR,v_X_SCL_FACTOR(ScaleCbrX) | v_Y_SCL_FACTOR(ScaleCbrY)); + lcdc_msk_reg(lcdc_dev,SYS_CTRL,m_WIN0_FORMAT,v_WIN0_FORMAT(par->format)); //(inf->video_mode==0) + lcdc_writel(lcdc_dev,WIN0_ACT_INFO,v_ACT_WIDTH(xact) | v_ACT_HEIGHT(yact)); + lcdc_writel(lcdc_dev,WIN0_DSP_ST,v_DSP_STX(xpos) | v_DSP_STY(ypos)); + lcdc_writel(lcdc_dev,WIN0_DSP_INFO,v_DSP_WIDTH(par->xsize) | v_DSP_HEIGHT(par->ysize)); + lcdc_msk_reg(lcdc_dev,WIN0_COLOR_KEY,m_COLOR_KEY_EN,v_COLOR_KEY_EN(0)); + + switch(par->format) + { + case ARGB888: + lcdc_msk_reg(lcdc_dev, WIN_VIR,m_WIN0_VIR,v_ARGB888_VIRWIDTH(xvir)); + //lcdc_msk_reg(lcdc_dev,SYS_CTRL1,m_W0_RGB_RB_SWAP,v_W0_RGB_RB_SWAP(1)); + break; + case RGB888: //rgb888 + lcdc_msk_reg(lcdc_dev, WIN_VIR,m_WIN0_VIR,v_RGB888_VIRWIDTH(xvir)); + //lcdc_msk_reg(lcdc_dev,SYS_CTRL1,m_W0_RGB_RB_SWAP,v_W0_RGB_RB_SWAP(1)); + break; + case RGB565: //rgb565 + lcdc_msk_reg(lcdc_dev, WIN_VIR,m_WIN0_VIR,v_RGB565_VIRWIDTH(xvir)); + break; + case YUV422: + case YUV420: + case YUV444: + lcdc_msk_reg(lcdc_dev, WIN_VIR,m_WIN0_VIR,v_YUV_VIRWIDTH(xvir)); + break; + default: + dev_err(lcdc_dev->driver.dev,"un supported format!\n"); + break; + } + + } + spin_unlock(&lcdc_dev->reg_lock); + + return 0; + +} + +static int win1_set_par(struct rk3188_lcdc_device *lcdc_dev,rk_screen *screen, + struct layer_par *par ) +{ + u32 xact, yact, xvir, yvir, xpos, ypos; + + xact = par->xact; + yact = par->yact; + xvir = par->xvir; + yvir = par->yvir; + xpos = par->xpos+screen->left_margin + screen->hsync_len; + ypos = par->ypos+screen->upper_margin + screen->vsync_len; + + + DBG(1,"%s for lcdc%d>>format:%d>>>xact:%d>>yact:%d>>xsize:%d>>ysize:%d>>xvir:%d>>yvir:%d>>xpos:%d>>ypos:%d>>\n", + __func__,lcdc_dev->id,par->format,xact,yact,par->xsize,par->ysize,xvir,yvir,xpos,ypos); + + + spin_lock(&lcdc_dev->reg_lock); + if(likely(lcdc_dev->clk_on)) + { + lcdc_msk_reg(lcdc_dev,SYS_CTRL,m_WIN1_FORMAT, v_WIN1_FORMAT(par->format)); + lcdc_writel(lcdc_dev, WIN1_DSP_INFO,v_DSP_WIDTH(par->xsize) | v_DSP_HEIGHT(par->ysize)); + lcdc_writel(lcdc_dev, WIN1_DSP_ST,v_DSP_STX(xpos) | v_DSP_STY(ypos)); + // disable win1 color key and set the color to black(rgb=0) + lcdc_msk_reg(lcdc_dev, WIN1_COLOR_KEY,m_COLOR_KEY_EN,v_COLOR_KEY_EN(0)); + switch(par->format) + { + case ARGB888: + lcdc_msk_reg(lcdc_dev, WIN_VIR,m_WIN1_VIR,v_WIN1_ARGB888_VIRWIDTH(xvir)); + //lcdc_msk_reg(lcdc_dev,SYS_CTRL1,m_W1_RGB_RB_SWAP,v_W1_RGB_RB_SWAP(1)); + break; + case RGB888: //rgb888 + lcdc_msk_reg(lcdc_dev, WIN_VIR,m_WIN1_VIR,v_WIN1_RGB888_VIRWIDTH(xvir)); + // lcdc_msk_reg(lcdc_dev,SYS_CTRL1,m_W1_RGB_RB_SWAP,v_W1_RGB_RB_SWAP(1)); + break; + case RGB565: //rgb565 + lcdc_msk_reg(lcdc_dev, WIN_VIR,m_WIN1_VIR,v_WIN1_RGB565_VIRWIDTH(xvir)); + break; + default: + dev_err(lcdc_dev->driver.dev,"un supported format!\n"); + break; + } + + } + spin_unlock(&lcdc_dev->reg_lock); + + return 0; +} + + +static int rk3188_lcdc_set_par(struct rk_lcdc_device_driver *dev_drv,int layer_id) +{ + struct rk3188_lcdc_device *lcdc_dev = + container_of(dev_drv,struct rk3188_lcdc_device,driver); + struct layer_par *par = NULL; + rk_screen *screen = dev_drv->cur_screen; + + if(!screen) + { + dev_err(dev_drv->dev,"screen is null!\n"); + return -ENOENT; + } + if(layer_id==0) + { + par = dev_drv->layer_par[0]; + win0_set_par(lcdc_dev,screen,par); + } + else if(layer_id==1) + { + par = dev_drv->layer_par[1]; + win1_set_par(lcdc_dev,screen,par); + } + else + { + dev_err(dev_drv->dev,"unsupported win number:%d\n",layer_id); + return -EINVAL; + } + + return 0; +} + +static int win0_display(struct rk3188_lcdc_device *lcdc_dev,struct layer_par *par ) +{ + u32 y_addr; + u32 uv_addr; + y_addr = par->smem_start + par->y_offset; + uv_addr = par->cbr_start + par->c_offset; + DBG(2,KERN_INFO "lcdc%d>>%s:y_addr:0x%x>>uv_addr:0x%x\n",lcdc_dev->id,__func__,y_addr,uv_addr); + + spin_lock(&lcdc_dev->reg_lock); + if(likely(lcdc_dev->clk_on)) + { + lcdc_writel(lcdc_dev, WIN0_YRGB_MST0, y_addr); + lcdc_writel(lcdc_dev, WIN0_CBR_MST0, uv_addr); + lcdc_cfg_done(lcdc_dev); + } + spin_unlock(&lcdc_dev->reg_lock); + + return 0; + +} + +static int win1_display(struct rk3188_lcdc_device *lcdc_dev,struct layer_par *par ) +{ + u32 y_addr; + u32 uv_addr; + y_addr = par->smem_start + par->y_offset; + uv_addr = par->cbr_start + par->c_offset; + DBG(2,KERN_INFO "lcdc%d>>%s>>y_addr:0x%x>>uv_addr:0x%x\n",lcdc_dev->id,__func__,y_addr,uv_addr); + + spin_lock(&lcdc_dev->reg_lock); + if(likely(lcdc_dev->clk_on)) + { + lcdc_writel(lcdc_dev,WIN1_MST,y_addr); + lcdc_cfg_done(lcdc_dev); + } + spin_unlock(&lcdc_dev->reg_lock); + + return 0; +} + +static int rk3188_lcdc_pan_display(struct rk_lcdc_device_driver * dev_drv,int layer_id) +{ + struct rk3188_lcdc_device *lcdc_dev = + container_of(dev_drv,struct rk3188_lcdc_device,driver); + struct layer_par *par = NULL; + rk_screen *screen = dev_drv->cur_screen; + unsigned long flags; + int timeout; + + if(!screen) + { + dev_err(dev_drv->dev,"screen is null!\n"); + return -ENOENT; + } + if(layer_id==0) + { + par = dev_drv->layer_par[0]; + win0_display(lcdc_dev,par); + } + else if(layer_id==1) + { + par = dev_drv->layer_par[1]; + win1_display(lcdc_dev,par); + } + else + { + dev_err(dev_drv->dev,"invalid win number:%d!\n",layer_id); + return -EINVAL; + } + if((dev_drv->first_frame)) //this is the first frame of the system ,enable frame start interrupt + { + dev_drv->first_frame = 0; + lcdc_msk_reg(lcdc_dev,INT_STATUS,m_FS_INT_CLEAR |m_FS_INT_EN , + v_FS_INT_CLEAR(1) | v_FS_INT_EN(1)); + lcdc_cfg_done(lcdc_dev); // write any value to REG_CFG_DONE let config become effective + + } + +#if defined(WAIT_FOR_SYNC) + spin_lock_irqsave(&dev_drv->cpl_lock,flags); + init_completion(&dev_drv->frame_done); + spin_unlock_irqrestore(&dev_drv->cpl_lock,flags); + timeout = wait_for_completion_timeout(&dev_drv->frame_done,msecs_to_jiffies(dev_drv->cur_screen->ft+5)); + if(!timeout&&(!dev_drv->frame_done.done)) + { + printk(KERN_ERR "wait for new frame start time out!\n"); + return -ETIMEDOUT; + } +#endif + return 0; +} + +static int rk3188_lcdc_blank(struct rk_lcdc_device_driver *dev_drv, + int layer_id,int blank_mode) +{ + return 0; +} + + +static int rk3188_lcdc_ioctl(struct rk_lcdc_device_driver *dev_drv, unsigned int cmd,unsigned long arg,int layer_id) +{ + return 0; +} + +static int rk3188_lcdc_early_suspend(struct rk_lcdc_device_driver *dev_drv) +{ + return 0; +} + +static int rk3188_lcdc_early_resume(struct rk_lcdc_device_driver *dev_drv) +{ + return 0; +} + +static int rk3188_lcdc_get_layer_state(struct rk_lcdc_device_driver *dev_drv,int layer_id) +{ + return 0; +} + +static int rk3188_lcdc_ovl_mgr(struct rk_lcdc_device_driver *dev_drv,int swap,bool set) +{ + return 0; +} + +static ssize_t rk3188_lcdc_get_disp_info(struct rk_lcdc_device_driver *dev_drv,char *buf,int layer_id) +{ + return 0; +} + +static int rk3188_lcdc_fps_mgr(struct rk_lcdc_device_driver *dev_drv,int fps,bool set) +{ + return 0; +} + + +static int rk3188_fb_layer_remap(struct rk_lcdc_device_driver *dev_drv, + enum fb_win_map_order order) +{ + mutex_lock(&dev_drv->fb_win_id_mutex); + if(order == FB_DEFAULT_ORDER ) + { + order = FB0_WIN0_FB1_WIN1_FB2_WIN2; + } + dev_drv->fb2_win_id = order/100; + dev_drv->fb1_win_id = (order/10)%10; + dev_drv->fb0_win_id = order%10; + mutex_unlock(&dev_drv->fb_win_id_mutex); + + printk("fb0:win%d\nfb1:win%d\nfb2:win%d\n",dev_drv->fb0_win_id,dev_drv->fb1_win_id, + dev_drv->fb2_win_id); + + return 0; +} + +static int rk3188_fb_get_layer(struct rk_lcdc_device_driver *dev_drv,const char *id) +{ + int layer_id = 0; + mutex_lock(&dev_drv->fb_win_id_mutex); + if(!strcmp(id,"fb0")||!strcmp(id,"fb2")) + { + layer_id = dev_drv->fb0_win_id; + } + else if(!strcmp(id,"fb1")||!strcmp(id,"fb3")) + { + layer_id = dev_drv->fb1_win_id; + } + mutex_unlock(&dev_drv->fb_win_id_mutex); + + return layer_id; +} + +static int rk3188_set_dsp_lut(struct rk_lcdc_device_driver *dev_drv,int *lut) +{ + int i=0; + int __iomem *c; + int v; + int ret = 0; + + struct rk3188_lcdc_device *lcdc_dev = + container_of(dev_drv,struct rk3188_lcdc_device,driver); + lcdc_msk_reg(lcdc_dev,SYS_CTRL,m_DSP_LUT_EN,v_DSP_LUT_EN(0)); + lcdc_cfg_done(lcdc_dev); + msleep(25); + if(dev_drv->cur_screen->dsp_lut) + { + for(i=0;i<256;i++) + { + v = dev_drv->cur_screen->dsp_lut[i] = lut[i]; + c = lcdc_dev->dsp_lut_addr_base+i; + writel_relaxed(v,c); + + } + } + else + { + dev_err(dev_drv->dev,"no buffer to backup lut data!\n"); + ret = -1; + } + lcdc_msk_reg(lcdc_dev,SYS_CTRL,m_DSP_LUT_EN,v_DSP_LUT_EN(1)); + lcdc_cfg_done(lcdc_dev); + + return ret; +} + +static struct layer_par lcdc_layer[] = { + [0] = { + .name = "win0", + .id = 0, + .support_3d = true, + }, + [1] = { + .name = "win1", + .id = 1, + .support_3d = false, + }, +}; + +static struct rk_lcdc_device_driver lcdc_driver = { + .name = "lcdc", + .def_layer_par = lcdc_layer, + .num_layer = ARRAY_SIZE(lcdc_layer), + .open = rk3188_lcdc_open, + .init_lcdc = rk3188_lcdc_init, + .load_screen = rk3188_load_screen, + .set_par = rk3188_lcdc_set_par, + .pan_display = rk3188_lcdc_pan_display, + .blank = rk3188_lcdc_blank, + .ioctl = rk3188_lcdc_ioctl, + .suspend = rk3188_lcdc_early_suspend, + .resume = rk3188_lcdc_early_resume, + .get_layer_state = rk3188_lcdc_get_layer_state, + .ovl_mgr = rk3188_lcdc_ovl_mgr, + .get_disp_info = rk3188_lcdc_get_disp_info, + .fps_mgr = rk3188_lcdc_fps_mgr, + .fb_get_layer = rk3188_fb_get_layer, + .fb_layer_remap = rk3188_fb_layer_remap, + .set_dsp_lut = rk3188_set_dsp_lut, +}; + +static irqreturn_t rk3188_lcdc_isr(int irq, void *dev_id) +{ + struct rk3188_lcdc_device *lcdc_dev = + (struct rk3188_lcdc_device *)dev_id; + + lcdc_msk_reg(lcdc_dev, INT_STATUS, m_FS_INT_CLEAR, v_FS_INT_CLEAR(1)); + +#if defined(WAIT_FOR_SYNC) + if(lcdc_dev->driver.num_buf < 3) //three buffer ,no need to wait for sync + { + spin_lock(&(lcdc_dev->driver.cpl_lock)); + complete(&(lcdc_dev->driver.frame_done)); + spin_unlock(&(lcdc_dev->driver.cpl_lock)); + } +#endif + return IRQ_HANDLED; +} + + +#if defined(CONFIG_PM) +static int rk3188_lcdc_suspend(struct platform_device *pdev, + pm_message_t state) +{ + return 0; +} + +static int rk3188_lcdc_resume(struct platform_device *pdev) +{ + return 0; +} +#else +#define rk3188_lcdc_suspend NULL +#define rk3188_lcdc_resume NULL +#endif +static int __devinit rk3188_lcdc_probe(struct platform_device *pdev) +{ + struct rk3188_lcdc_device *lcdc_dev = NULL; + struct device *dev = &pdev->dev; + rk_screen *screen; + struct rk29fb_info *screen_ctr_info; + struct resource *res = NULL; + struct resource *mem = NULL; + int ret = 0; + + lcdc_dev = devm_kzalloc(dev,sizeof(struct rk3188_lcdc_device), GFP_KERNEL); + if(!lcdc_dev) + { + dev_err(&pdev->dev, ">>rk3188 lcdc device kmalloc fail!"); + return -ENOMEM; + } + platform_set_drvdata(pdev, lcdc_dev); + lcdc_dev->id = pdev->id; + screen_ctr_info = (struct rk29fb_info * )pdev->dev.platform_data; + if(!screen_ctr_info) + { + dev_err(dev, "no platform data specified for screen control info!\n"); + ret = -EINVAL; + goto err0; + } + screen = kzalloc(sizeof(rk_screen), GFP_KERNEL); + if(!screen) + { + dev_err(&pdev->dev, "rk screen kmalloc fail!"); + ret = -ENOMEM; + goto err0; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM,0); + if (res == NULL) + { + dev_err(&pdev->dev, "failed to get register resource for lcdc%d \n",lcdc_dev->id); + ret = -ENOENT; + goto err1; + } + + lcdc_dev->reg_phy_base = res->start; + lcdc_dev->len = resource_size(res); + mem = request_mem_region(lcdc_dev->reg_phy_base,lcdc_dev->len, pdev->name); + if (!mem) + { + dev_err(&pdev->dev, "failed to request mem region for lcdc%d\n",lcdc_dev->id); + ret = -ENOENT; + goto err1; + } + lcdc_dev->regs = ioremap(lcdc_dev->reg_phy_base,lcdc_dev->len); + if (!lcdc_dev->regs) + { + dev_err(&pdev->dev, "cannot map register for lcdc%d\n",lcdc_dev->id); + ret = -ENXIO; + goto err2; + } + + lcdc_dev->regsbak = kzalloc(lcdc_dev->len,GFP_KERNEL); + if(!lcdc_dev->regsbak) + { + dev_err(&pdev->dev, "failed to map memory for reg backup!\n"); + ret = -ENOMEM; + goto err3; + } + lcdc_dev->dsp_lut_addr_base = (lcdc_dev->regs + DSP_LUT_ADDR); + printk("lcdc%d:reg_phy_base = 0x%08x,reg_vir_base:0x%p\n",pdev->id,lcdc_dev->reg_phy_base, lcdc_dev->regs); + lcdc_dev->driver.dev = dev; + lcdc_dev->driver.screen0 = screen; + lcdc_dev->driver.cur_screen = screen; + lcdc_dev->driver.screen_ctr_info = screen_ctr_info; + + spin_lock_init(&lcdc_dev->reg_lock); + + lcdc_dev->irq = platform_get_irq(pdev, 0); + if(lcdc_dev->irq < 0) + { + dev_err(&pdev->dev, "cannot find IRQ for lcdc%d\n",lcdc_dev->id); + goto err3; + } + ret = devm_request_irq(dev,lcdc_dev->irq, rk3188_lcdc_isr, IRQF_DISABLED,dev_name(dev),lcdc_dev); + if (ret) + { + dev_err(&pdev->dev, "cannot requeset irq %d - err %d\n", lcdc_dev->irq, ret); + ret = -EBUSY; + goto err3; + } + ret = rk_fb_register(&(lcdc_dev->driver),&lcdc_driver,lcdc_dev->id); + if(ret < 0) + { + dev_err(dev,"register fb for lcdc%d failed!\n",lcdc_dev->id); + goto err4; + } + printk("rk3188 lcdc%d probe ok!\n",lcdc_dev->id); + +err4: + free_irq(lcdc_dev->irq,lcdc_dev); +err3: + iounmap(lcdc_dev->regs); +err2: + release_mem_region(lcdc_dev->reg_phy_base,lcdc_dev->len); +err1: + kfree(screen); +err0: + platform_set_drvdata(pdev, NULL); + kfree(lcdc_dev); + + return ret; +} + +static int __devexit rk3188_lcdc_remove(struct platform_device *pdev) +{ + return 0; +} + +static void rk3188_lcdc_shutdown(struct platform_device *pdev) +{ + +} +static struct platform_driver rk3188_lcdc_driver = { + .probe = rk3188_lcdc_probe, + .remove = __devexit_p(rk3188_lcdc_remove), + .driver = { + .name = "rk3188-lcdc", + .owner = THIS_MODULE, + }, + .suspend = rk3188_lcdc_suspend, + .resume = rk3188_lcdc_resume, + .shutdown = rk3188_lcdc_shutdown, +}; +static int __init rk3188_lcdc_module_init(void) +{ + return platform_driver_register(&rk3188_lcdc_driver); +} + +static void __exit rk3188_lcdc_module_exit(void) +{ + platform_driver_unregister(&rk3188_lcdc_driver); +} +fs_initcall(rk3188_lcdc_module_init); +module_exit(rk3188_lcdc_module_exit); diff --git a/drivers/video/rockchip/lcdc/rk3188_lcdc.h b/drivers/video/rockchip/lcdc/rk3188_lcdc.h new file mode 100644 index 000000000000..c30f24d96ec4 --- /dev/null +++ b/drivers/video/rockchip/lcdc/rk3188_lcdc.h @@ -0,0 +1,371 @@ +#ifndef RK3188_LCDC_H_ +#define RK3188_LCDC_H_ + +#include +#include +#include + + +/*******************register definition**********************/ + +#define SYS_CTRL (0x00) +#define m_WIN0_EN (1<<0) +#define m_WIN1_EN (1<<1) +#define m_HWC_EN (1<<2) +#define m_WIN0_FORMAT (7<<3) +#define m_WIN1_FORMAT (7<<6) +#define m_HWC_COLOR_MODE (1<<9) +#define m_HWC_SIZE (1<<10) +#define m_WIN0_3D_EN (1<11) +#define m_WIN0_3D_MODE (7<<12) +#define m_WIN0_RB_SWAP (1<<15) +#define m_WIN0_ALPHA_SWAP (1<<16) +#define m_WIN0_Y8_SWAP (1<<17) +#define m_WIN0_UV_SWAP (1<<18) +#define m_WIN1_RB_SWAP (1<<19) +#define m_WIN1_ALPHA_SWAP (1<<20) +#define m_WIN1_BL_SWAP (1<<21) +#define m_WIN0_OTSD_DISABLE (1<<22) +#define m_WIN1_OTSD_DISABLE (1<<23) +#define m_DMA_BURST_LENGTH (3<<24) +#define m_HWC_LODAD_EN (1<<26) +#define m_WIN1_LUT_EN (1<<27) +#define m_DSP_LUT_EN (1<<28) +#define m_DMA_STOP (1<<29) +#define m_LCDC_STANDBY (1<<30) +#define m_AUTO_GATING_EN (1<<31) +#define v_WIN0_EN(x) (((x)&1)<<0) +#define v_WIN1_EN(x) (((x)&1)<<1) +#define v_HWC_EN(x) (((x)&1)<<2) +#define v_WIN0_FORMAT(x) (((x)&7)<<3) +#define v_WIN1_FORMAT(x) (((x)&7)<<6) +#define v_HWC_COLOR_MODE(x) (((x)&1)<<9) +#define v_HWC_SIZE(x) (((x)&1)<<10) +#define v_WIN0_3D_EN(x) (((x)&1)<11) +#define v_WIN0_3D_MODE(x) (((x)&7)<<12) +#define v_WIN0_RB_SWAP(x) (((x)&1)<<15) +#define v_WIN0_ALPHA_SWAP(x) (((x)&1)<<16) +#define v_WIN0_Y8_SWAP(x) (((x)&1)<<17) +#define v_WIN0_UV_SWAP(x) (((x)&1)<<18) +#define v_WIN1_RB_SWAP(x) (((x)&1)<<19) +#define v_WIN1_ALPHA_SWAP(x) (((x)&1)<<20) +#define v_WIN1_BL_SWAP(x) (((x)&1)<<21) +#define v_WIN0_OTSD_DISABLE(x) (((x)&1)<<22) +#define v_WIN1_OTSD_DISABLE(x) (((x)&1)<<23) +#define v_DMA_BURST_LENGTH(x) (((x)&3)<<24) +#define v_HWC_LODAD_EN(x) (((x)&1)<<26) +#define v_WIN1_LUT_EN(x) (((x)&1)<<27) +#define v_DSP_LUT_EN(x) (((x)&1)<<28) +#define v_DMA_STOP(x) (((x)&1)<<29) +#define v_LCDC_STANDBY(x) (((x)&1)<<30) +#define v_AUTO_GATING_EN(x) (((x)&1)<<31) + + +#define DSP_CTRL0 (0x04) +#define m_DSP_OUT_FORMAT (0x0f<<0) +#define m_HSYNC_POL (1<<4) +#define m_VSYNC_POL (1<<5) +#define m_DEN_POL (1<<6) +#define m_DCLK_POL (1<<7) +#define m_WIN0_TOP (1<<8) +#define m_DITHER_UP_EN (1<<9) +#define m_DITHER_DOWN_MODE (1<<10) +#define m_DITHER_DOWN_EN (1<<11) +#define m_INTERLACE_DSP_EN (1<<12) +#define m_INTERLACE_POL (1<<13) +#define m_WIN0_INTERLACE_EN (1<<14) +#define m_WIN1_INTERLACE_EN (1<<15) +#define m_WIN0_YRGB_DEFLICK_EN (1<<16) +#define m_WIN0_CBR_DEFLICK_EN (1<<17) +#define m_WIN0_ALPHA_MODE (1<<18) +#define m_WIN1_ALPHA_MODE (1<<19) +#define m_WIN0_CSC_MODE (3<<20) +#define m_WIN1_CSC_MODE (1<<22) +#define m_WIN0_YUV_CLIP (1<<23) +#define m_DSP_CCIR656_AVG (1<<24) +#define m_DCLK_OUTPUT_MODE (1<<25) +#define m_DCLK_PHASE_LOCK (1<<26) +#define m_DITHER_DOWN_SEL (3<<27) +#define m_ALPHA_MODE_SEL0 (1<<29) +#define m_ALPHA_MODE_SEL1 (1<<30) +#define m_DIFF_DCLK_EN (1<<31) +#define v_DSP_OUT_FORMAT(x) (((x)&0x0f)<<0) +#define v_HSYNC_POL(x) (((x)&1)<<4) +#define v_VSYNC_POL(x) (((x)&1)<<5) +#define v_DEN_POL(x) (((x)&1)<<6) +#define v_DCLK_POL(x) (((x)&1)<<7) +#define v_WIN0_TOP(x) (((x)&1)<<8) +#define v_DITHER_UP_EN(x) (((x)&1)<<9) +#define v_DITHER_DOWN_MODE(x) (((x)&1)<<10) +#define v_DITHER_DOWN_EN(x) (((x)&1)<<11) +#define v_INTERLACE_DSP_EN(x) (((x)&1)<<12) +#define v_INTERLACE_POL(x) (((x)&1)<<13) +#define v_WIN0_INTERLACE_EN(x) (((x)&1)<<14) +#define v_WIN1_INTERLACE_EN(x) (((x)&1)<<15) +#define v_WIN0_YRGB_DEFLICK_EN(x) (((x)&1)<<16) +#define v_WIN0_CBR_DEFLICK_EN(x) (((x)&1)<<17) +#define v_WIN0_ALPHA_MODE(x) (((x)&1)<<18) +#define v_WIN1_ALPHA_MODE(x) (((x)&1)<<19) +#define v_WIN0_CSC_MODE(x) (((x)&3)<<20) +#define v_WIN1_CSC_MODE(x) (((x)&1)<<22) +#define v_WIN0_YUV_CLIP(x) (((x)&1)<<23) +#define v_DSP_CCIR656_AVG(x) (((x)&1)<<24) +#define v_DCLK_OUTPUT_MODE(x) (((x)&1)<<25) +#define v_DCLK_PHASE_LOCK(x) (((x)&1)<<26) +#define v_DITHER_DOWN_SEL(x) (((x)&1)<<27) +#define v_ALPHA_MODE_SEL0(x) (((x)&1)<<29) +#define v_ALPHA_MODE_SEL1(x) (((x)&1)<<30) +#define v_DIFF_DCLK_EN(x) (((x)&1)<<31) + + +#define DSP_CTRL1 (0x08) +#define m_BG_COLOR (0xffffff<<0) +#define m_BG_B (0xff<<0) +#define m_BG_G (0xff<<8) +#define m_BG_R (0xff<<16) +#define m_BLANK_EN (1<<24) +#define m_BLACK_EN (1<<25) +#define m_DSP_BG_SWAP (1<<26) +#define m_DSP_RB_SWAP (1<<27) +#define m_DSP_RG_SWAP (1<<28) +#define m_DSP_DELTA_SWAP (1<<29) +#define m_DSP_DUMMY_SWAP (1<<30) +#define m_DSP_OUT_ZERO (1<<31) +#define v_BG_COLOR(x) (((x)&0xffffff)<<0) +#define v_BG_B(x) (((x)&0xff)<<0) +#define v_BG_G(x) (((x)&0xff)<<8) +#define v_BG_R(x) (((x)&0xff)<<16) +#define v_BLANK_EN(x) (((x)&1)<<24) +#define v_BLACK_EN(x) (((x)&1)<<25) +#define v_DSP_BG_SWAP(x) (((x)&1)<<26) +#define v_DSP_RB_SWAP(x) (((x)&1)<<27) +#define v_DSP_RG_SWAP(x) (((x)&1)<<28) +#define v_DSP_DELTA_SWAP(x) (((x)&1)<<29) +#define v_DSP_DUMMY_SWAP(x) (((x)&1)<<30) +#define v_DSP_OUT_ZERO(x) (((x)&1)<<31) + + +#define MCU_CTRL (0x0c) +#define m_MCU_PIX_TOTAL (0x3f<<0) +#define m_MCU_CS_ST (0x0f<<6) +#define m_MCU_CS_END (0x3f<<10) +#define m_MCU_RW_ST (0x0f<<16) +#define m_MCU_RW_END (0x3f<<20) +#define m_MCU_CLK_SEL (1<<26) +#define m_MCU_HOLD_MODE (1<<27) +#define m_MCU_FS_HOLD_STA (1<<28) +#define m_MCU_RS_SELECT (1<<29) +#define m_MCU_BYPASS (1<<30) +#define m_MCU_TYPE (1<<31) + +#define v_MCU_PIX_TOTAL(x) (((x)&0x3f)<<0) +#define v_MCU_CS_ST(x) (((x)&0x0f)<<6) +#define v_MCU_CS_END(x) (((x)&0x3f)<<10) +#define v_MCU_RW_ST(x) (((x)&0x0f)<<16) +#define v_MCU_RW_END(x) (((x)&0x3f)<<20) +#define v_MCU_CLK_SEL(x) (((x)&1)<<26) +#define v_MCU_HOLD_MODE(x) (((x)&1)<<27) +#define v_MCU_FS_HOLD_STA(x) (((x)&1)<<28) +#define v_MCU_RS_SELECT(x) (((x)&1)<<29) +#define v_MCU_BYPASS(x) (((x)&1)<<30) +#define v_MCU_TYPE(x) (((x)&1)<<31) + +#define INT_STATUS (0x10) +#define m_HS_INT_STA (1<<0) //status +#define m_FS_INT_STA (1<<1) +#define m_LF_INT_STA (1<<2) +#define m_BUS_ERR_INT_STA (1<<3) +#define m_HS_INT_EN (1<<4) //enable +#define m_FS_INT_EN (1<<5) +#define m_LF_INT_EN (1<<6) +#define m_BUS_ERR_INT_EN (1<<7) +#define m_HS_INT_CLEAR (1<<8) //auto clear +#define m_FS_INT_CLEAR (1<<9) +#define m_LF_INT_CLEAR (1<<10) +#define m_BUS_ERR_INT_CLEAR (1<<11) +#define m_LINE_FLAG_NUM (0xfff<<12) +#define v_HS_INT_EN(x) (((x)&1)<<4) +#define v_FS_INT_EN(x) (((x)&1)<<5) +#define v_LF_INT_EN(x) (((x)&1)<<6) +#define v_BUS_ERR_INT_EN(x) (((x)&1)<<7) +#define v_HS_INT_CLEAR(x) (((x)&1)<<8) +#define v_FS_INT_CLEAR(x) (((x)&1)<<9) +#define v_LF_INT_CLEAR(x) (((x)&1)<<10) +#define v_BUS_ERR_INT_CLEAR(x) (((x)&1)<<11) +#define v_LINE_FLAG_NUM(x) (((x)&0xfff)<<12) + + +#define ALPHA_CTRL (0x14) +#define m_WIN0_ALPHA_EN (1<<0) +#define m_WIN1_ALPAH_EN (1<<1) +#define m_HWC_ALPAH_EN (1<<2) +#define m_WIN0_ALPHA_VAL (0xff<<4) +#define m_WIN1_ALPHA_VAL (0xff<<12) +#define m_HWC_ALPAH_VAL (0x0f<<20) +#define v_WIN0_ALPHA_EN(x) (((x)&1)<<0) +#define v_WIN1_ALPAH_EN(x) (((x)&1)<<1) +#define v_HWC_ALPAH_EN(x) (((x)&1)<<2) +#define v_WIN0_ALPHA_VAL(x) (((x)&0xff)<<4) +#define v_WIN1_ALPHA_VAL(x) (((x)&0xff)<<12) +#define v_HWC_ALPAH_VAL(x) (((x)&0x0f)<<20) + +#define WIN0_COLOR_KEY (0x18) +#define m_COLOR_KEY_VAL (0xffffff<<0) +#define m_COLOR_KEY_EN (1<<24) +#define v_COLOR_KEY_VAL(x) (((x)&0xffffff)<<0) +#define v_COLOR_KEY_EN(x) (((x)&1)<<24) + +#define WIN1_COLOR_KEY (0x1C) + + +#define WIN0_YRGB_MST0 (0x20) +#define WIN0_CBR_MST0 (0x24) +#define WIN0_YRGB_MST1 (0x28) +#define WIN0_CBR_MST1 (0x2C) +#define WIN_VIR (0x30) +#define m_WIN0_VIR (0x1fff << 0) +#define m_WIN1_VIR (0x1fff << 16) +#define v_ARGB888_VIRWIDTH(x) (((x)&0x1fff)<<0) +#define v_RGB888_VIRWIDTH(x) (((((x*3)>>2)+((x)%3))&0x1fff)<<0) +#define v_RGB565_VIRWIDTH(x) ((DIV_ROUND_UP(x,2)&0x1fff)<<0) +#define v_YUV_VIRWIDTH(x) ((DIV_ROUND_UP(x,4)&0x1fff)<<0) +#define v_WIN1_ARGB888_VIRWIDTH(x) (((x)&0x1fff)<<0) +#define v_WIN1_RGB888_VIRWIDTH(x) (((((x*3)>>2)+((x)%3))&0x1fff)<<0) +#define v_WIN1_RGB565_VIRWIDTH(x) ((DIV_ROUND_UP(x,2)&0x1fff)<<0) + + + +#define WIN0_ACT_INFO (0x34) +#define m_ACT_WIDTH (0x1fff<<0) +#define m_ACT_HEIGHT (0x1fff<<16) +#define v_ACT_WIDTH(x) (((x)&0x1fff)<<0) +#define v_ACT_HEIGHT(x) (((x)&0x1fff)<<16) + +#define WIN0_DSP_INFO (0x38) +#define v_DSP_WIDTH(x) (((x-1)&0x7ff)<<0) +#define v_DSP_HEIGHT(x) (((x-1)&0x7ff)<<16) + +#define WIN0_DSP_ST (0x3C) +#define v_DSP_STX(x) (((x)&0xfff)<<0) +#define v_DSP_STY(x) (((x)&0xfff)<<16) + +#define WIN0_SCL_FACTOR_YRGB (0x40) +#define v_X_SCL_FACTOR(x) (((x)&0xffff)<<0) +#define v_Y_SCL_FACTOR(x) (((x)&0xffff)<<16) + +#define WIN0_SCL_FACTOR_CBR (0x44) +#define WIN0_SCL_OFFSET (0x48) +#define WIN1_MST (0x4C) +#define WIN1_DSP_INFO (0x50) +#define WIN1_DSP_ST (0x54) +#define HWC_MST (0x58) +#define HWC_DSP_ST (0x5C) +#define HWC_COLOR_LUT0 (0x60) +#define HWC_COLOR_LUT1 (0x64) +#define HWC_COLOR_LUT2 (0x68) +#define DSP_HTOTAL_HS_END (0x6C) +#define v_HSYNC(x) (((x)&0xfff)<<0) //hsync pulse width +#define v_HORPRD(x) (((x)&0xfff)<<16) //horizontal period + +#define DSP_HACT_ST_END (0x70) +#define v_HAEP(x) (((x)&0xfff)<<0) //horizontal active end point +#define v_HASP(x) (((x)&0xfff)<<16) //horizontal active start point + +#define DSP_VTOTAL_VS_END (0x74) +#define v_VSYNC(x) (((x)&0xfff)<<0) +#define v_VERPRD(x) (((x)&0xfff)<<16) +#define DSP_VACT_ST_END (0x78) +#define v_VAEP(x) (((x)&0xfff)<<0) +#define v_VASP(x) (((x)&0xfff)<<16) + +#define DSP_VS_ST_END_F1 (0x7C) +#define DSP_VACT_ST_END_F1 (0x80) +#define REG_CFG_DONE (0x90) +#define MCU_BYPASS_WPORT (0x100) +#define MCU_BYPASS_RPORT (0x200) +#define WIN1_LUT_ADDR (0x400) +#define DSP_LUT_ADDR (0x800) + + +#define CalScale(x, y) ((((u32)x)*0x1000)/y) + +struct rk3188_lcdc_device{ + int id; + struct rk_lcdc_device_driver driver; + rk_screen *screen; + + void __iomem *regs; + void *regsbak; //back up reg + u32 reg_phy_base; // physical basic address of lcdc register + u32 len; // physical map length of lcdc register + spinlock_t reg_lock; //one time only one process allowed to config the register + + int __iomem *dsp_lut_addr_base; + + + bool clk_on; //if aclk or hclk is closed ,acess to register is not allowed + u8 atv_layer_cnt; //active layer counter,when atv_layer_cnt = 0,disable lcdc + + unsigned int irq; + + struct clk *pd; //lcdc power domain + struct clk *hclk; //lcdc AHP clk + struct clk *dclk; //lcdc dclk + struct clk *aclk; //lcdc share memory frequency + u32 pixclock; +}; + + + +static inline void lcdc_writel(struct rk3188_lcdc_device *lcdc_dev,u32 offset,u32 v) +{ + u32 *_pv = (u32*)lcdc_dev->regsbak; + _pv += (offset >> 2); + *_pv = v; + writel_relaxed(v,lcdc_dev->regs+offset); +} + +static inline u32 lcdc_readl(struct rk3188_lcdc_device *lcdc_dev,u32 offset) +{ + return readl_relaxed(lcdc_dev->regs+offset); +} + +static inline u32 lcdc_read_bit(struct rk3188_lcdc_device *lcdc_dev,u32 offset,u32 msk) +{ + u32 _v = readl_relaxed(lcdc_dev->regs+offset); + _v &= msk; + return (_v >> msk); +} + +static inline void lcdc_set_bit(struct rk3188_lcdc_device *lcdc_dev,u32 offset,u32 msk) +{ + u32* _pv = (u32*)lcdc_dev->regsbak; + _pv += (offset >> 2); + (*_pv) |= msk; + writel_relaxed(*_pv,lcdc_dev->regs + offset); +} + +static inline void lcdc_clr_bit(struct rk3188_lcdc_device *lcdc_dev,u32 offset,u32 msk) +{ + u32* _pv = (u32*)lcdc_dev->regsbak; + _pv += (offset >> 2); + (*_pv) &= (~msk); + writel_relaxed(*_pv,lcdc_dev->regs + offset); +} + +static inline void lcdc_msk_reg(struct rk3188_lcdc_device *lcdc_dev,u32 offset,u32 msk,u32 v) +{ + u32 *_pv = (u32*)lcdc_dev->regsbak; + _pv += (offset >> 2); + (*_pv) &= (~msk); + (*_pv) |= v; + writel_relaxed(*_pv,lcdc_dev->regs+offset); +} + +static inline void lcdc_cfg_done(struct rk3188_lcdc_device *lcdc_dev) +{ + writel_relaxed(0x01,lcdc_dev->regs+REG_CFG_DONE); + dsb(); +} + +#endif diff --git a/include/linux/rk_fb.h b/include/linux/rk_fb.h index feb79735ff00..b3e30117809e 100644 --- a/include/linux/rk_fb.h +++ b/include/linux/rk_fb.h @@ -20,8 +20,8 @@ #include #include #include -#include - +#include +#include #define RK30_MAX_LCDC_SUPPORT 4 #define RK30_MAX_LAYER_SUPPORT 4 @@ -52,21 +52,20 @@ #define FBIOGET_ENABLE 0x5020 /******************************************************************** -** display output interface supported by rk lcdc * +** display output interface supported by rockchip lcdc * ********************************************************************/ /* */ -#define OUT_P888 0 -#define OUT_P666 1 +#define OUT_P888 0 //24bit screen,connect to lcdc D0~D23 +#define OUT_P666 1 //18bit screen,connect to lcdc D0~D17 #define OUT_P565 2 #define OUT_S888x 4 #define OUT_CCIR656 6 #define OUT_S888 8 #define OUT_S888DUMY 12 #define OUT_P16BPP4 24 -#define OUT_D888_P666 0x21 +#define OUT_D888_P666 0x21 //18bit screen,connect to lcdc D2~D7, D10~D15, D18~D23 #define OUT_D888_P565 0x22 - /** * pixel format definitions,this is copy from android/system/core/include/system/graphics.h */ @@ -144,8 +143,8 @@ enum data_format{ enum fb_win_map_order{ FB_DEFAULT_ORDER = 0, - FB0_WIN2_FB1_WIN1_FB2_WIN0 = 012, - FB0_WIN1_FB1_WIN2_FB2_WIN0 = 021, + FB0_WIN2_FB1_WIN1_FB2_WIN0 = 12, + FB0_WIN1_FB1_WIN2_FB2_WIN0 = 21, FB0_WIN2_FB1_WIN0_FB2_WIN1 = 102, FB0_WIN0_FB1_WIN2_FB2_WIN1 = 120, FB0_WIN0_FB1_WIN1_FB2_WIN2 = 210, @@ -237,6 +236,7 @@ struct rk_lcdc_device_driver{ int (*set_dsp_lut)(struct rk_lcdc_device_driver *dev_drv,int *lut); int (*read_dsp_lut)(struct rk_lcdc_device_driver *dev_drv,int *lut); int (*lcdc_hdmi_process)(struct rk_lcdc_device_driver *dev_drv,int mode); //some lcdc need to some process in hdmi mode + int (*lcdc_rst)(struct rk_lcdc_device_driver *dev_drv); }; @@ -260,4 +260,26 @@ extern struct rk_lcdc_device_driver * rk_get_lcdc_drv(char *name); extern int rk_fb_switch_screen(rk_screen *screen ,int enable ,int lcdc_id); extern int rk_fb_disp_scale(u8 scale_x, u8 scale_y,u8 lcdc_id); extern int rkfb_create_sysfs(struct fb_info *fbi); +static int inline rk_fb_calc_fps(rk_screen *screen,u32 pixclock) +{ + int x, y; + unsigned long long hz; + if(!screen) + { + printk(KERN_ERR "%s:null screen!\n",__func__); + return 0; + } + x = screen->x_res + screen->left_margin + screen->right_margin + screen->hsync_len; + y = screen->y_res + screen->upper_margin + screen->lower_margin + screen->vsync_len; + + hz = 1000000000000ULL; /* 1e12 picoseconds per second */ + + hz += (x * y) / 2; + do_div(hz, x * y); /* divide by x * y with rounding */ + + hz += pixclock / 2; + do_div(hz,pixclock); /* divide by pixclock with rounding */ + + return hz; +} #endif diff --git a/include/linux/rk_screen.h b/include/linux/rk_screen.h index 977a0891fd0e..6315d7d60fe9 100644 --- a/include/linux/rk_screen.h +++ b/include/linux/rk_screen.h @@ -109,9 +109,9 @@ typedef struct rk29fb_screen { u8 pin_dispon; /* Swap rule */ - u8 swap_rb; + u8 swap_bg; u8 swap_rg; - u8 swap_gb; + u8 swap_rb; u8 swap_delta; u8 swap_dumy; -- 2.34.1