From b6d7152f51d613b4c16a83701416f45252b2e9e5 Mon Sep 17 00:00:00 2001 From: Zheng Yang Date: Tue, 8 Jul 2014 18:11:59 +0800 Subject: [PATCH] rk3036: add tv encoder code. --- arch/arm/boot/dts/rk3036-sdk.dts | 12 +- arch/arm/boot/dts/rk3036.dtsi | 17 +- drivers/video/rockchip/Makefile | 2 + drivers/video/rockchip/lcdc/rk3036_lcdc.c | 351 ++++++++++++------ drivers/video/rockchip/lcdc/rk3036_lcdc.h | 6 +- drivers/video/rockchip/rk_fb.c | 144 ++++++- drivers/video/rockchip/tve/Kconfig | 23 +- drivers/video/rockchip/tve/Makefile | 7 +- drivers/video/rockchip/tve/rk3036/Kconfig | 7 + drivers/video/rockchip/tve/rk3036/Makefile | 4 + .../video/rockchip/tve/rk3036/rk3036_tve.c | 305 +++++++++++++++ .../video/rockchip/tve/rk3036/rk3036_tve.h | 100 +++++ drivers/video/rockchip/tve/rk610/Kconfig | 14 + drivers/video/rockchip/tve/rk610/Makefile | 6 + .../video/rockchip/tve/{ => rk610}/rk610_tv.c | 0 .../video/rockchip/tve/{ => rk610}/rk610_tv.h | 0 .../rockchip/tve/{ => rk610}/rk610_tv_cvbs.c | 0 .../rockchip/tve/{ => rk610}/rk610_tv_ypbpr.c | 0 include/linux/rk_fb.h | 6 +- 19 files changed, 843 insertions(+), 161 deletions(-) create mode 100644 drivers/video/rockchip/tve/rk3036/Kconfig create mode 100644 drivers/video/rockchip/tve/rk3036/Makefile create mode 100644 drivers/video/rockchip/tve/rk3036/rk3036_tve.c create mode 100644 drivers/video/rockchip/tve/rk3036/rk3036_tve.h create mode 100644 drivers/video/rockchip/tve/rk610/Kconfig create mode 100644 drivers/video/rockchip/tve/rk610/Makefile rename drivers/video/rockchip/tve/{ => rk610}/rk610_tv.c (100%) rename drivers/video/rockchip/tve/{ => rk610}/rk610_tv.h (100%) rename drivers/video/rockchip/tve/{ => rk610}/rk610_tv_cvbs.c (100%) rename drivers/video/rockchip/tve/{ => rk610}/rk610_tv_ypbpr.c (100%) diff --git a/arch/arm/boot/dts/rk3036-sdk.dts b/arch/arm/boot/dts/rk3036-sdk.dts index 38970e29a26f..a95b8a11d2b1 100755 --- a/arch/arm/boot/dts/rk3036-sdk.dts +++ b/arch/arm/boot/dts/rk3036-sdk.dts @@ -1,7 +1,8 @@ /dts-v1/; - +#include #include "rk3036.dtsi" #include "rk3036-pinctrl.dtsi" +#include "lcd-box.dtsi" / { fiq-debugger { @@ -137,3 +138,12 @@ //irq_gpio = <&gpio0 GPIO_A4 IRQ_TYPE_EDGE_FALLING>; }; }; + + +&rk_screen { + display-timings = <&disp_timings>; +}; + +&lcdc { + status = "okay"; +}; diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi index 8aa9102a1e89..9da50489462e 100755 --- a/arch/arm/boot/dts/rk3036.dtsi +++ b/arch/arm/boot/dts/rk3036.dtsi @@ -494,10 +494,19 @@ clock-names = "clk_usbphy1", "hclk_usb1"; }; + fb: fb{ + compatible = "rockchip,rk-fb"; + rockchip,disp-mode = ; + }; + + rk_screen: rk_screen{ + compatible = "rockchip,screen"; + }; + lcdc: lcdc@10118000 { compatible = "rockchip,rk3036-lcdc"; reg = <0x10118000 0x4000>; - interrupts = ; + interrupts = ; status = "disabled"; clocks = <&clk_gates9 6>, <&dclk_lcdc1>, <&clk_gates9 5>; clock-names = "aclk_lcdc", "dclk_lcdc", "hclk_lcdc"; @@ -517,6 +526,12 @@ status = "disabled"; }; + tve { + compatible = "rockchip,rk3036-tve"; + reg = <0x10118200 0x100>; + status = "disabled"; + }; + vpu: vpu_service@10108000 { compatible = "vpu_service"; reg = <0x10108000 0x800>; diff --git a/drivers/video/rockchip/Makefile b/drivers/video/rockchip/Makefile index e03c30460300..03f76012b533 100755 --- a/drivers/video/rockchip/Makefile +++ b/drivers/video/rockchip/Makefile @@ -7,4 +7,6 @@ obj-$(CONFIG_ROCKCHIP_RGA) += rga/ obj-$(CONFIG_ROCKCHIP_RGA2) += rga2/ obj-$(CONFIG_RK_HDMI) += display-sys.o hdmi/ obj-$(CONFIG_IEP) += iep/ +obj-$(CONFIG_RK_TVENCODER) += tve/ + diff --git a/drivers/video/rockchip/lcdc/rk3036_lcdc.c b/drivers/video/rockchip/lcdc/rk3036_lcdc.c index e78ce2ea8c08..526b742a1f73 100644 --- a/drivers/video/rockchip/lcdc/rk3036_lcdc.c +++ b/drivers/video/rockchip/lcdc/rk3036_lcdc.c @@ -33,7 +33,10 @@ #include #include #include - +#if defined(CONFIG_ION_ROCKCHIP) +#include +#include +#endif #include "rk3036_lcdc.h" static int dbg_thresd; @@ -61,8 +64,6 @@ static struct rk_lcdc_win lcdc_win[] = { }, }; -static void layer_enable(struct lcdc_device *lcdc_dev, unsigned int win_id, bool open); - static irqreturn_t rk3036_lcdc_isr(int irq, void *dev_id) { struct lcdc_device *lcdc_dev = @@ -169,48 +170,100 @@ static void rk_lcdc_read_reg_defalut_cfg(struct lcdc_device spin_unlock(&lcdc_dev->reg_lock); } -/********do basic init*********/ -static int rk3036_lcdc_pre_init(struct rk_lcdc_driver *dev_drv) +static int rk3036_lcdc_alpha_cfg(struct lcdc_device *lcdc_dev) { - u32 mask,val; - struct lcdc_device *lcdc_dev = container_of(dev_drv, - struct - lcdc_device, - driver); - if (lcdc_dev->pre_init) - return 0; + return 0; +} - lcdc_dev->hclk = devm_clk_get(lcdc_dev->dev, "hclk_lcdc"); - lcdc_dev->aclk = devm_clk_get(lcdc_dev->dev, "aclk_lcdc"); - lcdc_dev->dclk = devm_clk_get(lcdc_dev->dev, "dclk_lcdc"); -// lcdc_dev->pd = devm_clk_get(lcdc_dev->dev, "pd_lcdc"); +static void lcdc_layer_update_regs(struct lcdc_device *lcdc_dev, struct rk_lcdc_win *win) { - if (/*IS_ERR(lcdc_dev->pd) ||*/ (IS_ERR(lcdc_dev->aclk)) || - (IS_ERR(lcdc_dev->dclk)) || (IS_ERR(lcdc_dev->hclk))) { - dev_err(lcdc_dev->dev, "failed to get lcdc%d clk source\n", - lcdc_dev->id); + u32 mask, val; + + if(win->state == 1){ + if(win->id == 0) { + mask = m_WIN0_EN | m_WIN0_FORMAT | m_WIN0_RB_SWAP; + val = v_WIN0_EN(win->state) | v_WIN0_FORMAT(win->fmt_cfg) | v_WIN0_RB_SWAP(win->swap_rb); + lcdc_msk_reg(lcdc_dev, SYS_CTRL, mask, val); + + lcdc_writel(lcdc_dev, WIN0_SCL_FACTOR_YRGB, + v_X_SCL_FACTOR(win->scale_yrgb_x) | + v_Y_SCL_FACTOR(win->scale_yrgb_y)); + lcdc_writel(lcdc_dev, WIN0_SCL_FACTOR_CBR, + v_X_SCL_FACTOR(win->scale_cbcr_x) | + v_Y_SCL_FACTOR(win->scale_cbcr_y)); + + lcdc_msk_reg(lcdc_dev, WIN0_VIR, m_YRGB_VIR | m_CBBR_VIR, + v_YRGB_VIR(win->area[0].y_vir_stride) | v_YRGB_VIR(win->area[0].uv_vir_stride)); + lcdc_writel(lcdc_dev, WIN0_ACT_INFO, v_ACT_WIDTH(win->area[0].xact) | + v_ACT_HEIGHT(win->area[0].yact)); + lcdc_writel(lcdc_dev, WIN0_DSP_ST, v_DSP_STX(win->area[0].dsp_stx) | + v_DSP_STY(win->area[0].dsp_sty)); + lcdc_writel(lcdc_dev, WIN0_DSP_INFO, v_DSP_WIDTH(win->area[0].xsize) | + v_DSP_HEIGHT(win->area[0].ysize)); + + lcdc_writel(lcdc_dev, WIN0_YRGB_MST, win->area[0].y_addr); + lcdc_writel(lcdc_dev, WIN0_CBR_MST, win->area[0].uv_addr); + } + else if(win->id == 1) { + mask = m_WIN1_EN | m_WIN1_FORMAT | m_WIN1_RB_SWAP; + val = v_WIN1_EN(win->state) | v_WIN1_FORMAT(win->fmt_cfg) | v_WIN1_RB_SWAP(win->swap_rb); + lcdc_msk_reg(lcdc_dev, SYS_CTRL, mask, val); + + lcdc_writel(lcdc_dev, WIN1_SCL_FACTOR_YRGB, + v_X_SCL_FACTOR(win->scale_yrgb_x) | + v_Y_SCL_FACTOR(win->scale_yrgb_y)); + + lcdc_msk_reg(lcdc_dev, WIN1_VIR, m_YRGB_VIR, v_YRGB_VIR(win->area[0].y_vir_stride)); + lcdc_writel(lcdc_dev, WIN1_ACT_INFO, v_ACT_WIDTH(win->area[0].xact) | + v_ACT_HEIGHT(win->area[0].yact)); + lcdc_writel(lcdc_dev, WIN1_DSP_INFO, v_DSP_WIDTH(win->area[0].xsize) | + v_DSP_HEIGHT(win->area[0].ysize)); + lcdc_writel(lcdc_dev, WIN1_DSP_ST, v_DSP_STX(win->area[0].dsp_stx) | + v_DSP_STY(win->area[0].dsp_sty)); + + lcdc_writel(lcdc_dev, WIN1_MST, win->area[0].y_addr); + + } + else if(win->id == 2) { + } + }else{ + win->area[0].y_addr = 0; + win->area[0].uv_addr = 0; + if(win->id == 0) { + lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_WIN0_EN, v_WIN0_EN(0)); + } + else if(win->id == 1) + lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_WIN1_EN, v_WIN1_EN(0)); + else if(win->id == 2) + lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_HWC_EN, v_HWC_EN(0)); } - - rk_disp_pwr_enable(dev_drv); - rk3036_lcdc_clk_enable(lcdc_dev); - - /*backup reg config at uboot*/ - rk_lcdc_read_reg_defalut_cfg(lcdc_dev); - - mask = m_AUTO_GATING_EN; - val = v_AUTO_GATING_EN(0); - lcdc_msk_reg(lcdc_dev, SYS_CTRL, mask,val); - lcdc_cfg_done(lcdc_dev); - if (dev_drv->iommu_enabled) /*disable win0 to workaround iommu pagefault*/ - layer_enable(lcdc_dev, 0, 0); - lcdc_dev->pre_init = true; - - return 0; } -static int rk3036_lcdc_alpha_cfg(struct lcdc_device *lcdc_dev) +static void lcdc_layer_enable(struct lcdc_device *lcdc_dev, unsigned int win_id, bool open) { - return 0; + spin_lock(&lcdc_dev->reg_lock); + if (likely(lcdc_dev->clk_on) && lcdc_dev->driver.win[win_id]->state != open) { + if (open) { + if (!lcdc_dev->atv_layer_cnt) { + dev_info(lcdc_dev->dev, "wakeup from standby!\n"); + lcdc_dev->standby = 0; + } + lcdc_dev->atv_layer_cnt++; + } else if ((lcdc_dev->atv_layer_cnt > 0) && (!open)) { + lcdc_dev->atv_layer_cnt--; + } + lcdc_dev->driver.win[win_id]->state = open; + if(!open) { + lcdc_layer_update_regs(lcdc_dev, lcdc_dev->driver.win[win_id]); + lcdc_cfg_done(lcdc_dev); + } + /*if no layer used,disable lcdc*/ + if (!lcdc_dev->atv_layer_cnt) { + dev_info(lcdc_dev->dev, "no layer is used,go to standby!\n"); + lcdc_dev->standby = 1; + } + } + spin_unlock(&lcdc_dev->reg_lock); } static int rk3036_lcdc_reg_update(struct rk_lcdc_driver *dev_drv) @@ -225,37 +278,8 @@ static int rk3036_lcdc_reg_update(struct rk_lcdc_driver *dev_drv) if (likely(lcdc_dev->clk_on)) { lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_LCDC_STANDBY, v_LCDC_STANDBY(lcdc_dev->standby)); - lcdc_msk_reg(lcdc_dev, SYS_CTRL, - m_WIN0_EN | m_WIN1_EN | m_WIN0_RB_SWAP | - m_WIN1_RB_SWAP, - v_WIN0_EN(win0->state) | v_WIN1_EN(win1->state) | - v_WIN0_RB_SWAP(win0->swap_rb) | - v_WIN1_RB_SWAP(win1->swap_rb)); - lcdc_writel(lcdc_dev, WIN0_SCL_FACTOR_YRGB, - v_X_SCL_FACTOR(win0->scale_yrgb_x) | - v_Y_SCL_FACTOR(win0->scale_yrgb_y)); - lcdc_writel(lcdc_dev, WIN0_SCL_FACTOR_CBR, - v_X_SCL_FACTOR(win0->scale_cbcr_x) | - v_Y_SCL_FACTOR(win0->scale_cbcr_y)); - lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_WIN0_FORMAT, v_WIN0_FORMAT(win0->fmt_cfg)); - lcdc_writel(lcdc_dev, WIN0_ACT_INFO, v_ACT_WIDTH(win0->area[0].xact) | - v_ACT_HEIGHT(win0->area[0].yact)); - lcdc_writel(lcdc_dev, WIN0_DSP_ST, v_DSP_STX(win0->area[0].dsp_stx) | - v_DSP_STY(win0->area[0].dsp_sty)); - lcdc_writel(lcdc_dev, WIN0_DSP_INFO, v_DSP_WIDTH(win0->area[0].xsize) | - v_DSP_HEIGHT(win0->area[0].ysize)); - lcdc_msk_reg(lcdc_dev, WIN0_VIR, m_YRGB_VIR | m_CBBR_VIR, - v_YRGB_VIR(win0->area[0].y_vir_stride) | v_YRGB_VIR(win0->area[0].uv_vir_stride)); - lcdc_writel(lcdc_dev, WIN0_YRGB_MST, win0->area[0].y_addr); - lcdc_writel(lcdc_dev, WIN0_CBR_MST, win0->area[0].uv_addr); - lcdc_writel(lcdc_dev, WIN1_DSP_INFO, v_DSP_WIDTH(win1->area[0].xsize) | - v_DSP_HEIGHT(win1->area[0].ysize)); - lcdc_writel(lcdc_dev, WIN1_DSP_ST, v_DSP_STX(win1->area[0].dsp_stx) | - v_DSP_STY(win1->area[0].dsp_sty)); - lcdc_msk_reg(lcdc_dev, WIN1_VIR, m_YRGB_VIR, v_YRGB_VIR(win1->area[0].y_vir_stride)); - lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_WIN1_FORMAT, - v_WIN1_FORMAT(win1->fmt_cfg)); - lcdc_writel(lcdc_dev, WIN1_MST, win1->area[0].y_addr); + lcdc_layer_update_regs(lcdc_dev, win0); + lcdc_layer_update_regs(lcdc_dev, win1); rk3036_lcdc_alpha_cfg(lcdc_dev); lcdc_cfg_done(lcdc_dev); @@ -324,6 +348,39 @@ static int rk3036_lcdc_set_dclk(struct rk_lcdc_driver *dev_drv) } +/********do basic init*********/ +static int rk3036_lcdc_pre_init(struct rk_lcdc_driver *dev_drv) +{ + struct lcdc_device *lcdc_dev = container_of(dev_drv, + struct lcdc_device, driver); + if (lcdc_dev->pre_init) + return 0; + + lcdc_dev->hclk = devm_clk_get(lcdc_dev->dev, "hclk_lcdc"); + lcdc_dev->aclk = devm_clk_get(lcdc_dev->dev, "aclk_lcdc"); + lcdc_dev->dclk = devm_clk_get(lcdc_dev->dev, "dclk_lcdc"); +// lcdc_dev->pd = devm_clk_get(lcdc_dev->dev, "pd_lcdc"); + + if (/*IS_ERR(lcdc_dev->pd) ||*/ (IS_ERR(lcdc_dev->aclk)) || + (IS_ERR(lcdc_dev->dclk)) || (IS_ERR(lcdc_dev->hclk))) { + dev_err(lcdc_dev->dev, "failed to get lcdc%d clk source\n", + lcdc_dev->id); + } + + rk_disp_pwr_enable(dev_drv); + rk3036_lcdc_clk_enable(lcdc_dev); + + /*backup reg config at uboot*/ + rk_lcdc_read_reg_defalut_cfg(lcdc_dev); + lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_AUTO_GATING_EN,v_AUTO_GATING_EN(0)); + lcdc_cfg_done(lcdc_dev); + if (dev_drv->iommu_enabled) /*disable win0 to workaround iommu pagefault*/ + lcdc_layer_enable(lcdc_dev, 0, 0); + lcdc_dev->pre_init = true; + + return 0; +} + static int rk3036_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen) { int ret = -EINVAL; @@ -360,6 +417,14 @@ static int rk3036_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen) val |= v_CORE_CLK_DIV_EN(1); } lcdc_msk_reg(lcdc_dev, AXI_BUS_CTRL, mask, val); + if(x_res == 720 && y_res == 576) + lcdc_msk_reg(lcdc_dev, DSP_CTRL0, m_TVE_MODE, v_TVE_MODE(TV_PAL)); + else if(x_res == 720 && y_res == 480) + lcdc_msk_reg(lcdc_dev, DSP_CTRL0, m_TVE_MODE, v_TVE_MODE(TV_NTSC)); + else { + dev_err(lcdc_dev->dev, "unsupported video timing!\n"); + return -1; + } break; default: dev_err(lcdc_dev->dev, "un supported interface!\n"); @@ -396,17 +461,34 @@ static int rk3036_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen) v_HASP(screen->mode.hsync_len + left_margin); lcdc_writel(lcdc_dev, DSP_HACT_ST_END, val); - val = - v_VSYNC(screen->mode.vsync_len) | v_VERPRD(screen->mode. - vsync_len + - upper_margin + - y_res + - lower_margin); - lcdc_writel(lcdc_dev, DSP_VTOTAL_VS_END, val); - - val = v_VAEP(screen->mode.vsync_len + upper_margin + y_res) | - v_VASP(screen->mode.vsync_len + screen->mode.upper_margin); - lcdc_writel(lcdc_dev, DSP_VACT_ST_END, val); + if(screen->mode.vmode == FB_VMODE_INTERLACED) { + //First Field Timing + lcdc_writel(lcdc_dev, DSP_VTOTAL_VS_END, v_VSYNC(screen->mode.vsync_len) | + v_VERPRD(2 * (screen->mode.vsync_len + upper_margin + lower_margin) + y_res + 1)); + lcdc_writel(lcdc_dev,DSP_VACT_ST_END,v_VAEP(screen->mode.vsync_len + upper_margin + y_res/2)| + v_VASP(screen->mode.vsync_len + upper_margin)); + //Second Field Timing + lcdc_writel(lcdc_dev, DSP_VS_ST_END_F1, v_VSYNC_ST_F1(screen->mode.vsync_len + upper_margin + y_res/2 + lower_margin) | + v_VSYNC_END_F1(2 * screen->mode.vsync_len + upper_margin + y_res/2 + lower_margin)); + lcdc_writel(lcdc_dev,DSP_VACT_ST_END_F1,v_VAEP(2 * (screen->mode.vsync_len + upper_margin) + y_res + lower_margin + 1)| + v_VASP(2 * (screen->mode.vsync_len + upper_margin) + y_res/2 + lower_margin + 1)); + + lcdc_msk_reg(lcdc_dev, DSP_CTRL0, m_INTERLACE_DSP_EN | m_WIN1_INTERLACE_EN | m_WIN0_YRGB_DEFLICK_EN | m_WIN0_CBR_DEFLICK_EN, + v_INTERLACE_DSP_EN(1) | v_WIN1_INTERLACE_EN(1) | v_WIN0_YRGB_DEFLICK_EN(1) | v_WIN0_CBR_DEFLICK_EN(1) ); + } else { + val = v_VSYNC(screen->mode.vsync_len) | + v_VERPRD(screen->mode.vsync_len + upper_margin + + y_res + lower_margin); + lcdc_writel(lcdc_dev, DSP_VTOTAL_VS_END, val); + + val = v_VAEP(screen->mode.vsync_len + upper_margin + y_res) | + v_VASP(screen->mode.vsync_len + screen->mode.upper_margin); + lcdc_writel(lcdc_dev, DSP_VACT_ST_END, val); + + lcdc_msk_reg(lcdc_dev, DSP_CTRL0, m_INTERLACE_DSP_EN | m_WIN1_INTERLACE_EN | m_WIN0_YRGB_DEFLICK_EN | m_WIN0_CBR_DEFLICK_EN, + v_INTERLACE_DSP_EN(0) | v_WIN1_INTERLACE_EN(0) | v_WIN0_YRGB_DEFLICK_EN(0) | v_WIN0_CBR_DEFLICK_EN(0) ); + } + } spin_unlock(&lcdc_dev->reg_lock); @@ -429,35 +511,6 @@ static int rk3036_load_screen(struct rk_lcdc_driver *dev_drv, bool initscreen) return 0; } -static void layer_enable(struct lcdc_device *lcdc_dev, unsigned int win_id, bool open) -{ - spin_lock(&lcdc_dev->reg_lock); - if (likely(lcdc_dev->clk_on)) { - if (open) { - if (!lcdc_dev->atv_layer_cnt) { - dev_info(lcdc_dev->dev, "wakeup from standby!\n"); - lcdc_dev->standby = 0; - } - lcdc_dev->atv_layer_cnt++; - } else if ((lcdc_dev->atv_layer_cnt > 0) && (!open)) { - lcdc_dev->atv_layer_cnt--; - } - lcdc_dev->driver.win[win_id]->state = open; - if(win_id == 0) - lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_WIN0_EN, v_WIN0_EN(open)); - else if(win_id == 1) - lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_WIN1_EN, v_WIN1_EN(open)); - else - lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_HWC_EN, v_HWC_EN(open)); - /*if no layer used,disable lcdc*/ - if (!lcdc_dev->atv_layer_cnt) { - dev_info(lcdc_dev->dev, "no layer is used,go to standby!\n"); - lcdc_dev->standby = 1; - } - } - spin_unlock(&lcdc_dev->reg_lock); -} - static int rk3036_lcdc_open(struct rk_lcdc_driver *dev_drv, int win_id, bool open) { @@ -468,6 +521,20 @@ static int rk3036_lcdc_open(struct rk_lcdc_driver *dev_drv, int win_id, if ((open) && (!lcdc_dev->atv_layer_cnt)) { rk3036_lcdc_pre_init(dev_drv); rk3036_lcdc_clk_enable(lcdc_dev); + #if defined(CONFIG_ROCKCHIP_IOMMU) + if(dev_drv->iommu_enabled) { + if(!dev_drv->mmu_dev) { + dev_drv->mmu_dev = rockchip_get_sysmmu_device_by_compatible(dev_drv->mmu_dts_name); + if (dev_drv->mmu_dev) + platform_set_sysmmu(dev_drv->mmu_dev, dev_drv->dev); + else { + dev_err(dev_drv->dev, "failed to get rockchip iommu device\n"); + return -1; + } + } + iovmm_activate(dev_drv->dev); + } + #endif rk3036_lcdc_reg_restore(lcdc_dev); if (dev_drv->iommu_enabled) rk3036_lcdc_mmu_en(dev_drv); @@ -478,8 +545,9 @@ static int rk3036_lcdc_open(struct rk_lcdc_driver *dev_drv, int win_id, rk3036_load_screen(dev_drv, 1); } } + if(win_id < ARRAY_SIZE(lcdc_win)) { - layer_enable(lcdc_dev, win_id, open); + lcdc_layer_enable(lcdc_dev, win_id, open); } else dev_err(lcdc_dev->dev, "invalid win id:%d\n", win_id); @@ -488,6 +556,14 @@ static int rk3036_lcdc_open(struct rk_lcdc_driver *dev_drv, int win_id, if ((!open) && (!lcdc_dev->atv_layer_cnt)) { rk3036_lcdc_disable_irq(lcdc_dev); rk3036_lcdc_reg_update(dev_drv); + #if defined(CONFIG_ROCKCHIP_IOMMU) + if (dev_drv->iommu_enabled) { +// for (reg = MMU_DTE_ADDR; reg <= MMU_AUTO_GATING; reg +=4) +// lcdc_readl(lcdc_dev, reg); + if(dev_drv->mmu_dev) + iovmm_deactivate(dev_drv->dev); + } + #endif rk3036_lcdc_clk_disable(lcdc_dev); } @@ -517,9 +593,13 @@ static int rk3036_lcdc_set_par(struct rk_lcdc_driver *dev_drv,int win_id) } spin_lock(&lcdc_dev->reg_lock); - win->area[0].dsp_stx = win->area[0].xpos + screen->mode.left_margin + screen->mode.hsync_len; - win->area[0].dsp_sty = win->area[0].ypos + screen->mode.upper_margin + screen->mode.vsync_len; + if (screen->mode.vmode == FB_VMODE_INTERLACED) { + win->area[0].ysize /= 2; + win->area[0].dsp_sty = win->area[0].ypos/2+screen->mode.upper_margin + screen->mode.vsync_len; + } else { + win->area[0].dsp_sty = win->area[0].ypos + screen->mode.upper_margin + screen->mode.vsync_len; + } win->scale_yrgb_x = CalScale(win->area[0].xact, win->area[0].xsize); win->scale_yrgb_y = CalScale(win->area[0].yact, win->area[0].ysize); switch (win->format) { @@ -584,9 +664,6 @@ static int rk3036_lcdc_set_par(struct rk_lcdc_driver *dev_drv,int win_id) } spin_unlock(&lcdc_dev->reg_lock); - if (lcdc_dev->clk_on) { - rk3036_lcdc_alpha_cfg(lcdc_dev); - } DBG(1, "lcdc%d>>%s\n>>format:%s>>>xact:%d>>yact:%d>>xsize:%d>>ysize:%d\n" ">>xvir:%d>>yvir:%d>>xpos:%d>>ypos:%d>>\n", lcdc_dev->id, __func__, get_format_string(win->format, fmt), win->area[0].xact, @@ -621,11 +698,8 @@ static int rk3036_lcdc_pan_display(struct rk_lcdc_driver *dev_drv, int win_id) if (likely(lcdc_dev->clk_on)) { win->area[0].y_addr = win->area[0].smem_start+win->area[0].y_offset; win->area[0].uv_addr = win->area[0].cbr_start + win->area[0].c_offset; - if(win_id == 0) { - lcdc_writel(lcdc_dev, WIN0_YRGB_MST, win->area[0].y_addr); - lcdc_writel(lcdc_dev, WIN0_CBR_MST, win->area[0].uv_addr); - } else - lcdc_writel(lcdc_dev, WIN1_MST, win->area[0].y_addr); + if(win->area[0].y_addr) + lcdc_layer_update_regs(lcdc_dev, win); /*lcdc_cfg_done(lcdc_dev);*/ } spin_unlock(&lcdc_dev->reg_lock); @@ -741,6 +815,14 @@ static int rk3036_lcdc_early_suspend(struct rk_lcdc_driver *dev_drv) lcdc_msk_reg(lcdc_dev, SYS_CTRL, m_LCDC_STANDBY, v_LCDC_STANDBY(1)); lcdc_cfg_done(lcdc_dev); + #if defined(CONFIG_ROCKCHIP_IOMMU) + if (dev_drv->iommu_enabled) { +// for (reg = MMU_DTE_ADDR; reg <= MMU_AUTO_GATING; reg +=4) +// lcdc_readl(lcdc_dev, reg); + if(dev_drv->mmu_dev) + iovmm_deactivate(dev_drv->dev); + } + #endif spin_unlock(&lcdc_dev->reg_lock); } else { spin_unlock(&lcdc_dev->reg_lock); @@ -970,7 +1052,7 @@ static int rk3036_fb_win_remap(struct rk_lcdc_driver *dev_drv, { mutex_lock(&dev_drv->fb_win_id_mutex); if (order == FB_DEFAULT_ORDER) - order = FB0_WIN0_FB1_WIN1_FB2_WIN2; + order = FB0_WIN1_FB1_WIN0_FB2_WIN2; dev_drv->fb2_win_id = order / 100; dev_drv->fb1_win_id = (order / 10) % 10; dev_drv->fb0_win_id = order % 10; @@ -1125,6 +1207,22 @@ static struct rk_lcdc_drv_ops lcdc_drv_ops = { .open_bcsh = rk3036_lcdc_open_bcsh, }; +static int rk3036_lcdc_parse_dt(struct lcdc_device *lcdc_dev) +{ + struct device_node *np = lcdc_dev->dev->of_node; + +#if defined(CONFIG_ROCKCHIP_IOMMU) + int val; + if (of_property_read_u32(np, "rockchip,iommu-enabled", &val)) + lcdc_dev->driver.iommu_enabled = 0; + else + lcdc_dev->driver.iommu_enabled = val; +#else + lcdc_dev->driver.iommu_enabled = 0; +#endif + return 0; +} + static int rk3036_lcdc_probe(struct platform_device *pdev) { struct lcdc_device *lcdc_dev = NULL; @@ -1141,6 +1239,7 @@ static int rk3036_lcdc_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, lcdc_dev); lcdc_dev->dev = dev; + rk3036_lcdc_parse_dt(lcdc_dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); lcdc_dev->reg_phy_base = res->start; @@ -1176,7 +1275,11 @@ static int rk3036_lcdc_probe(struct platform_device *pdev) lcdc_dev->irq, ret); return ret; } - + + if (dev_drv->iommu_enabled) { + strcpy(dev_drv->mmu_dts_name, "iommu,vop_mmu"); + } + ret = rk_fb_register(dev_drv, lcdc_win, lcdc_dev->id); if (ret < 0) { dev_err(dev, "register fb for lcdc%d failed!\n", lcdc_dev->id); @@ -1184,7 +1287,9 @@ static int rk3036_lcdc_probe(struct platform_device *pdev) } lcdc_dev->screen = dev_drv->screen0; - dev_info(dev, "lcdc%d probe ok\n", lcdc_dev->id); + dev_info(dev, "lcdc%d probe ok, iommu %s\n", + lcdc_dev->id, dev_drv->iommu_enabled ? "enabled" : "disabled"); + return 0; } diff --git a/drivers/video/rockchip/lcdc/rk3036_lcdc.h b/drivers/video/rockchip/lcdc/rk3036_lcdc.h index bb0861ac8e01..9c37f9b7a285 100644 --- a/drivers/video/rockchip/lcdc/rk3036_lcdc.h +++ b/drivers/video/rockchip/lcdc/rk3036_lcdc.h @@ -252,6 +252,8 @@ #define v_VASP(x) (((x)&0xfff)<<16) #define DSP_VS_ST_END_F1 (0x7C) + #define v_VSYNC_END_F1(x) (((x)&0xfff)<<0) + #define v_VSYNC_ST_F1(x) (((x)&0xfff)<<16) #define DSP_VACT_ST_END_F1 (0x80) /*BCSH Registers*/ @@ -342,7 +344,7 @@ #define REG_CFG_DONE (0x90) /* TV Control Registers */ -#define TV_CTRL (0x200) +#define TV_CTRL (0x200) #define TV_SYNC_TIMING (0x204) #define TV_ACT_TIMING (0x208) #define TV_ADJ_TIMING (0x20c) @@ -484,7 +486,7 @@ struct lcdc_device{ struct clk *aclk; /*lcdc share memory frequency*/ u32 pixclock; - u32 standby; /*1:standby,0:wrok*/ + u32 standby; /*1:standby,0:work*/ }; static inline void lcdc_writel(struct lcdc_device *lcdc_dev,u32 offset,u32 v) diff --git a/drivers/video/rockchip/rk_fb.c b/drivers/video/rockchip/rk_fb.c index bf7e6ccf35ca..e6d061fface2 100755 --- a/drivers/video/rockchip/rk_fb.c +++ b/drivers/video/rockchip/rk_fb.c @@ -378,7 +378,7 @@ int rk_fb_prase_timing_dt(struct device_node *np, struct rk_screen *screen) pr_err("parse display timing err\n"); return -EINVAL; } - dt = display_timings_get(disp_timing, 0); + dt = display_timings_get(disp_timing, disp_timing->native_mode); rk_fb_video_mode_from_timing(dt, screen); return 0; @@ -2467,6 +2467,7 @@ static int rk_fb_set_par(struct fb_info *info) struct rk_lcdc_win *extend_win = NULL; struct rk_lcdc_win *win = NULL; struct rk_screen *screen = dev_drv->cur_screen; + struct rk_screen screen_primary; int win_id = 0; u32 cblen = 0, crlen = 0; u16 xsize = 0, ysize = 0; /* winx display window height/width --->LCDC_WINx_DSP_INFO */ @@ -2619,23 +2620,23 @@ static int rk_fb_set_par(struct fb_info *info) } } + rk_fb_get_prmry_screen(&screen_primary); win->format = fb_data_fmt; win->area[0].y_vir_stride = stride >> 2; win->area[0].uv_vir_stride = uv_stride >> 2; - win->area[0].xpos = xpos; - win->area[0].ypos = ypos; - win->area[0].xsize = xsize; - win->area[0].ysize = ysize; + win->area[0].xpos = xpos*screen->mode.xres/screen_primary.mode.xres; + win->area[0].ypos = ypos*screen->mode.yres/screen_primary.mode.yres; + win->area[0].xsize = screen->mode.xres*xsize/screen_primary.mode.xres; + win->area[0].ysize = screen->mode.yres*ysize/screen_primary.mode.yres; - win->area[0].smem_start = fix->smem_start; - win->area[0].cbr_start = fix->smem_start + stride * yvir; /* fix->mmio_start; */ +// win->area[0].smem_start = fix->smem_start; +// win->area[0].cbr_start = fix->smem_start+stride*yvir;//fix->mmio_start; win->area[0].xact = var->xres; /* winx active window height,is a wint of vir */ win->area[0].yact = var->yres; win->area[0].xvir = var->xres_virtual; /* virtual resolution stride --->LCDC_WINx_VIR */ win->area[0].yvir = var->yres_virtual; - win->area[0].state = 1; + win->area_num = 1; - win->state = 1; win->alpha_mode = 4; /* AB_SRC_OVER; */ win->alpha_en = ((win->format == ARGB888) || (win->format == ABGR888)) ? 1 : 0; @@ -2851,7 +2852,7 @@ void rk_direct_fb_show(struct fb_info *fbi) rk_fb_pan_display(&fbi->var, fbi); } EXPORT_SYMBOL(rk_direct_fb_show); - +#if 0 static int set_xact_yact_for_hdmi(struct fb_var_screeninfo *pmy_var, struct fb_var_screeninfo *hdmi_var) { @@ -2868,7 +2869,7 @@ static int set_xact_yact_for_hdmi(struct fb_var_screeninfo *pmy_var, return 0; } - +#endif int rk_fb_dpi_open(bool open) { struct rk_lcdc_driver *dev_drv = NULL; @@ -2899,7 +2900,109 @@ int rk_fb_dpi_status(void) return ret; } +#if 1 +/* + *function:this function will be called by display device, enable/disable lcdc + *screen: screen timing to be set to lcdc + *enable: 0 disable lcdc; 1 enable change lcdc timing; 2 just enable dclk + *lcdc_id: the lcdc id the display device attached ,0 or 1 + */ +int rk_fb_switch_screen(struct rk_screen *screen, int enable, int lcdc_id) +{ + struct rk_fb *rk_fb = platform_get_drvdata(fb_pdev); + struct fb_info *info = NULL; + struct rk_lcdc_driver *dev_drv = NULL; + char name[6]; + int i, win_id; + + if(screen == NULL) + return -ENODEV; + + printk("\n%s lcdc_id %d type %d enable %d\n", __FUNCTION__, lcdc_id, screen->type, enable); + + sprintf(name, "lcdc%d",lcdc_id); + for(i = 0; i < rk_fb->num_lcdc; i++) { + if(!strcmp(rk_fb->lcdc_dev_drv[i]->name, name)) { + dev_drv = rk_fb->lcdc_dev_drv[i]; + break; + } + } + if(i == rk_fb->num_lcdc) { + printk(KERN_ERR "%s driver not found!", name); + return -ENODEV; + } + + if(enable == 2 /*&& dev_drv->enable*/) + return 0; + + if(!enable) + { + // if screen type is different, we do not disable lcdc. + if( dev_drv->cur_screen->type != screen->type ) + return 0; + + for(i = 0; i < dev_drv->lcdc_win_num; i++) { + //disable the layer which attached to this device + if(dev_drv->win[i] && dev_drv->win[i]->state) + dev_drv->ops->open(dev_drv, i, 0); + } + +// dev_drv->enable = enable; + return 0; + } + else + memcpy(dev_drv->cur_screen, screen, sizeof(struct rk_screen)); + +// dev_drv->enable = enable; + +// ret = dev_drv->ops->load_screen(dev_drv, 1); +// +// if(enable != 1) return 0; + + for(i = 0; i < dev_drv->lcdc_win_num; i++) { + #ifdef DUAL_LCDC_MAP_TO_SAME_FB + info = rk_fb->fb[i]; + dev_drv1 = (struct rk_lcdc_driver *)info->par; + if(dev_drv1 != dev_drv) { + info->par = dev_drv; +// dev_drv->overlay = dev_drv1->overlay; + dev_drv->overscan = dev_drv1->overscan; + dev_drv->vsync_info.active = dev_drv1->vsync_info.active; + } + memcpy(dev_drv->cur_screen, screen, sizeof(struct rk_screen)); + #else + info = rk_fb->fb[dev_drv->fb_index_base + i]; + #endif + win_id = dev_drv->ops->fb_get_win_id(dev_drv,info->fix.id); +// printk("%s win id %d\n", info->fix.id, win_id); + if(dev_drv->win[win_id]) { + #ifdef DUAL_LCDC_MAP_TO_SAME_FB + if(dev_drv1 && dev_drv1->win[win_id]) { + dev_drv->win[win_id]->logicalstate = dev_drv1->win[win_id]->logicalstate; + memcpy(dev_drv->win[win_id]->area, dev_drv1->win[win_id]->area, RK_WIN_MAX_AREA * sizeof(struct rk_lcdc_win_area)); +// dev_drv->win[win_id]->area[0] = dev_drv1->win[win_id]->area[0]; +// printk("%s win id %d state %d\n", info->fix.id, win_id, dev_drv->win[win_id]->logicalstate); + } + #endif + printk("%s win id %d state %d\n", info->fix.id, win_id, dev_drv->win[win_id]->logicalstate); + if( dev_drv->win[win_id]->logicalstate ) { + dev_drv->ops->open(dev_drv, win_id, 1); + if(win_id == 1) { + dev_drv->ops->load_screen(dev_drv, 1); + } + info->var.activate |= FB_ACTIVATE_FORCE; + info->fbops->fb_set_par(info); + info->fbops->fb_pan_display(&info->var, info); + } +// else +// dev_drv->ops->open(dev_drv, win_id, 0); + } + } + printk("\n%s lcdc_id %d type %d enable %d done\n\n", __FUNCTION__, lcdc_id, screen->type, enable); + return 0; +} +#else /* * function:this function will be called by hdmi,when * hdmi plug in/out @@ -3043,7 +3146,7 @@ int rk_fb_switch_screen(struct rk_screen *screen, int enable, int lcdc_id) return 0; } - +#endif /* * function:this function current only called by hdmi for * scale the display @@ -3326,12 +3429,17 @@ static int init_lcdc_device_driver(struct rk_fb *rk_fb, mutex_init(&dev_drv->fb_win_id_mutex); dev_drv->ops->fb_win_remap(dev_drv, FB_DEFAULT_ORDER); dev_drv->first_frame = 1; + dev_drv->overscan.left = 100; + dev_drv->overscan.top = 100; + dev_drv->overscan.right = 100; + dev_drv->overscan.bottom = 100; rk_disp_pwr_ctr_parse_dt(dev_drv); if (dev_drv->prop == PRMRY) { - dev_drv->ops->set_dsp_cabc(dev_drv, dev_drv->cabc_mode); + if(dev_drv->ops->set_dsp_cabc) + dev_drv->ops->set_dsp_cabc(dev_drv, dev_drv->cabc_mode); rk_fb_set_prmry_screen(screen); - rk_fb_get_prmry_screen(screen); } + rk_fb_get_prmry_screen(screen); dev_drv->trsm_ops = rk_fb_trsm_ops_get(screen->type); return 0; @@ -3384,9 +3492,9 @@ int rk_fb_register(struct rk_lcdc_driver *dev_drv, struct rk_fb *rk_fb = platform_get_drvdata(fb_pdev); struct fb_info *fbi; int i = 0, ret = 0, index = 0; -#if defined(CONFIG_ROCKCHIP_IOMMU) - struct device *mmu_dev = NULL; -#endif +//#if defined(CONFIG_ROCKCHIP_IOMMU) +// struct device *mmu_dev = NULL; +//#endif if (rk_fb->num_lcdc == RK30_MAX_LCDC_SUPPORT) return -ENXIO; @@ -3491,6 +3599,7 @@ int rk_fb_register(struct rk_lcdc_driver *dev_drv, if (dev_drv->prop == PRMRY) { struct fb_info *main_fbi = rk_fb->fb[0]; main_fbi->fbops->fb_open(main_fbi, 1); +/* #if defined(CONFIG_ROCKCHIP_IOMMU) if (dev_drv->iommu_enabled) { mmu_dev = @@ -3505,6 +3614,7 @@ int rk_fb_register(struct rk_lcdc_driver *dev_drv, "failed to get rockchip iommu device\n"); } #endif +*/ rk_fb_alloc_buffer(main_fbi, 0); /* only alloc memory for main fb */ if (support_uboot_display()) { if (dev_drv->iommu_enabled) { diff --git a/drivers/video/rockchip/tve/Kconfig b/drivers/video/rockchip/tve/Kconfig index 74eaf28651be..e165ee3250ef 100644 --- a/drivers/video/rockchip/tve/Kconfig +++ b/drivers/video/rockchip/tve/Kconfig @@ -1,14 +1,13 @@ -config RK610_TVOUT - bool "RK610(Jetta) tvout support" - depends on MFD_RK610 - default n +# +# TV Encoder drivers configuration +# + +menuconfig RK_TVENCODER + bool "Rockchip TV Encoder support" + depends on FB_ROCKCHIP help - Support Jetta(RK610) to output YPbPr and CVBS. - -config RK610_TVOUT_YPbPr - bool "support YPbPr output" - depends on RK610_TVOUT + Support RockChip TV Encoder if you say y here. + +source "drivers/video/rockchip/tve/rk3036/Kconfig" +source "drivers/video/rockchip/tve/rk610/Kconfig" -config RK610_TVOUT_CVBS - bool "support CVBS output" - depends on RK610_TVOUT diff --git a/drivers/video/rockchip/tve/Makefile b/drivers/video/rockchip/tve/Makefile index b7d457326d26..ea651687c417 100644 --- a/drivers/video/rockchip/tve/Makefile +++ b/drivers/video/rockchip/tve/Makefile @@ -1,6 +1,5 @@ # -# Makefile for the jetta tv control. +# Makefile for tv encoder. # -obj-$(CONFIG_RK610_TVOUT) += rk610_tv.o -obj-$(CONFIG_RK610_TVOUT_YPbPr) += rk610_tv_ypbpr.o -obj-$(CONFIG_RK610_TVOUT_CVBS) += rk610_tv_cvbs.o \ No newline at end of file +obj-$(CONFIG_RK610_TVOUT) += rk610/ +obj-$(CONFIG_RK3036_TV_ENCODER) += rk3036/ diff --git a/drivers/video/rockchip/tve/rk3036/Kconfig b/drivers/video/rockchip/tve/rk3036/Kconfig new file mode 100644 index 000000000000..c39ed5d6dabd --- /dev/null +++ b/drivers/video/rockchip/tve/rk3036/Kconfig @@ -0,0 +1,7 @@ +config RK3036_TV_ENCODER + bool "rk3036 tv encoder support" + depends on LCDC_RK3036 + default n + help + Support RK3036 output CVBS. + diff --git a/drivers/video/rockchip/tve/rk3036/Makefile b/drivers/video/rockchip/tve/rk3036/Makefile new file mode 100644 index 000000000000..850404a1bdd3 --- /dev/null +++ b/drivers/video/rockchip/tve/rk3036/Makefile @@ -0,0 +1,4 @@ +# +# Makefile for the rk3036 tv encoder control. +# +obj-$(CONFIG_RK3036_TV_ENCODER) += rk3036_tve.o diff --git a/drivers/video/rockchip/tve/rk3036/rk3036_tve.c b/drivers/video/rockchip/tve/rk3036/rk3036_tve.c new file mode 100644 index 000000000000..ebe29da48406 --- /dev/null +++ b/drivers/video/rockchip/tve/rk3036/rk3036_tve.c @@ -0,0 +1,305 @@ +/* + * rk3036_tve.c + * + * Driver for rockchip rk3036 tv encoder control + * Copyright (C) 2014 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "rk3036_tve.h" + + +static const struct fb_videomode rk3036_cvbs_mode [] = { + /* name refresh xres yres pixclock h_bp h_fp v_bp v_fp h_pw v_pw polariry PorI flag*/ + { "NTSC", 60, 720, 480, 27000000, 69, 12, 19, 2, 63, 3, 0, FB_VMODE_INTERLACED, 0}, + { "PAL", 50, 720, 576, 27000000, 57, 19, 19, 0, 62, 3, 0, FB_VMODE_INTERLACED, 0}, +}; + +static struct rk3036_tve *rk3036_tve = NULL; + +#define tve_writel(offset, v) writel_relaxed(v, rk3036_tve->regbase + offset); +#define tve_readl(offset, v) readl_relaxed(v, rk3036_tve->regbase + offset); + +static void dac_enable(bool enable) +{ + u32 mask, val; + if(enable) { + mask = m_VBG_EN | m_DAC_EN; + val = mask; + } else { + mask = m_VBG_EN | m_DAC_EN; + val = 0; + } + grf_writel(RK3036_GRF_SOC_CON3, (mask << 16) | val); +} + +static void tve_set_mode (int mode) +{ + tve_writel(TV_RESET, v_RESET(1)); + udelay(100); + tve_writel(TV_RESET, v_RESET(0)); + + tve_writel(TV_CTRL, v_CVBS_MODE(mode) | v_CLK_UPSTREAM_EN(2) | + v_TIMING_EN(2) | v_LUMA_FILTER_GAIN(0) | + v_LUMA_FILTER_UPSAMPLE(1) | v_CSC_PATH(0) ); + tve_writel(TV_LUMA_FILTER0, 0x02ff0000); + tve_writel(TV_LUMA_FILTER1, 0xF40202fd); + tve_writel(TV_LUMA_FILTER2, 0xF332d919); + + if(mode == TVOUT_CVBS_NTSC) { + tve_writel(TV_ROUTING, v_DAC_SENSE_EN(0) | v_Y_IRE_7_5(0) | + v_Y_AGC_PULSE_ON(1) | v_Y_VIDEO_ON(1) | + v_Y_SYNC_ON(1) | v_PIC_MODE(mode)); + tve_writel(TV_BW_CTRL, v_CHROMA_BW(BP_FILTER_NTSC) | v_COLOR_DIFF_BW(COLOR_DIFF_FILTER_BW_1_3)); + tve_writel(TV_SATURATION, 0x0052543C); + tve_writel(TV_BRIGHTNESS_CONTRAST, 0x00008300); + + tve_writel(TV_FREQ_SC, 0x21F07BD7); + tve_writel(TV_SYNC_TIMING, 0x00C07a81); + tve_writel(TV_ADJ_TIMING, 0x96B40000); + tve_writel(TV_ACT_ST, 0x001500D6); + tve_writel(TV_ACT_TIMING, 0x169800FC); + + } else if (mode == TVOUT_CVBS_PAL) { + tve_writel(TV_ROUTING, v_DAC_SENSE_EN(0) | v_Y_IRE_7_5(1) | + v_Y_AGC_PULSE_ON(1) | v_Y_VIDEO_ON(1) | + v_Y_SYNC_ON(1) | v_CVBS_MODE(mode)); + tve_writel(TV_BW_CTRL, v_CHROMA_BW(BP_FILTER_PAL) | v_COLOR_DIFF_BW(COLOR_DIFF_FILTER_BW_1_3)); + tve_writel(TV_SATURATION, 0x002e553c); + tve_writel(TV_BRIGHTNESS_CONTRAST, 0x00007579); + + tve_writel(TV_FREQ_SC, 0x2A098ACB); + tve_writel(TV_SYNC_TIMING, 0x00C28381); + tve_writel(TV_ADJ_TIMING, 0xB6C00880); + tve_writel(TV_ACT_ST, 0x001500F6); + tve_writel(TV_ACT_TIMING, 0x2694011D); + } +} + +static int tve_switch_fb(const struct fb_videomode *modedb, int enable) +{ + struct rk_screen *screen; + + if(modedb == NULL) + return -1; + screen = kzalloc(sizeof(struct rk_screen), GFP_KERNEL); + if(screen == NULL) + return -1; + + memset(screen, 0, sizeof(struct rk_screen)); + /* screen type & face */ + screen->type = SCREEN_TVOUT; + screen->face = OUT_P888; + + screen->mode = *modedb; + + /* Pin polarity */ + if(FB_SYNC_HOR_HIGH_ACT & modedb->sync) + screen->pin_hsync = 1; + else + screen->pin_hsync = 0; + if(FB_SYNC_VERT_HIGH_ACT & modedb->sync) + screen->pin_vsync = 1; + else + screen->pin_vsync = 0; + + screen->pin_den = 0; + screen->pin_dclk = 1; + screen->pixelrepeat = 1; + + /* Swap rule */ + screen->swap_rb = 0; + screen->swap_rg = 0; + screen->swap_gb = 0; + screen->swap_delta = 0; + screen->swap_dumy = 0; + + /* Operation function*/ + screen->init = NULL; + screen->standby = NULL; + + rk_fb_switch_screen(screen, enable, 0); + + kfree(screen); + if(enable) { + if(screen->mode.yres == 480) + tve_set_mode(TVOUT_CVBS_NTSC); + else + tve_set_mode(TVOUT_CVBS_PAL); + } + return 0; +} + +static int cvbs_set_enable(struct rk_display_device *device, int enable) +{ + if(rk3036_tve->enable != enable) + { + rk3036_tve->enable = enable; + if(rk3036_tve->suspend) + return 0; + + if(enable == 0) { + dac_enable(false); + tve_switch_fb(rk3036_tve->mode, 0); + } + else if(enable == 1) { + tve_switch_fb(rk3036_tve->mode, 1); + dac_enable(true); + } + } + return 0; +} + +static int cvbs_get_enable(struct rk_display_device *device) +{ + return rk3036_tve->enable; +} + +static int cvbs_get_status(struct rk_display_device *device) +{ + return 1; +} + +static int cvbs_get_modelist(struct rk_display_device *device, struct list_head **modelist) +{ + *modelist = &(rk3036_tve->modelist); + return 0; +} + +static int cvbs_set_mode(struct rk_display_device *device, struct fb_videomode *mode) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(rk3036_cvbs_mode); i++) + { + if(fb_mode_is_equal(&rk3036_cvbs_mode[i], mode)) + { + if( rk3036_tve->mode != &rk3036_cvbs_mode[i] ) { + rk3036_tve->mode = (struct fb_videomode *)&rk3036_cvbs_mode[i]; + if(rk3036_tve->enable && !rk3036_tve->suspend) + dac_enable(false); + tve_switch_fb(rk3036_tve->mode, 1); + dac_enable(true); + } + return 0; + } + } + + return -1; +} + +static int cvbs_get_mode(struct rk_display_device *device, struct fb_videomode *mode) +{ + *mode = *(rk3036_tve->mode); + return 0; +} + +static struct rk_display_ops cvbs_display_ops = { + .setenable = cvbs_set_enable, + .getenable = cvbs_get_enable, + .getstatus = cvbs_get_status, + .getmodelist = cvbs_get_modelist, + .setmode = cvbs_set_mode, + .getmode = cvbs_get_mode, +}; + +static int display_cvbs_probe(struct rk_display_device *device, void *devdata) +{ + device->owner = THIS_MODULE; + strcpy(device->type, "TV"); + device->name = "cvbs"; + device->priority = DISPLAY_PRIORITY_TV; + device->priv_data = devdata; + device->ops = &cvbs_display_ops; + return 1; +} + +static struct rk_display_driver display_cvbs = { + .probe = display_cvbs_probe, +}; + +static int rk3036_tve_probe(struct platform_device *pdev) +{ + struct resource *res; + int i; + + rk3036_tve = devm_kzalloc(&pdev->dev, sizeof(struct rk3036_tve), + GFP_KERNEL); + if(!rk3036_tve) { + dev_err(&pdev->dev, "rk3036 tv encoder device kmalloc fail!"); + return -ENOMEM; + } + platform_set_drvdata(pdev, rk3036_tve); + rk3036_tve->dev = &pdev->dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + rk3036_tve->reg_phy_base = res->start; + rk3036_tve->len = resource_size(res); + rk3036_tve->regbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(rk3036_tve->regbase)) { + dev_err(&pdev->dev, "rk3036 tv encoder device map registers failed!"); + return PTR_ERR(rk3036_tve->regbase); + } + + INIT_LIST_HEAD(&(rk3036_tve->modelist)); + for(i = 0; i < ARRAY_SIZE(rk3036_cvbs_mode); i++) + fb_add_videomode(&rk3036_cvbs_mode[i], &(rk3036_tve->modelist)); + + rk3036_tve->ddev = rk_display_device_register(&display_cvbs, &pdev->dev, NULL); + rk_display_device_enable(rk3036_tve->ddev); + + dev_info(&pdev->dev, "rk3036 tv encoder probe ok\n"); + return 0; +} + +static void rk3036_tve_shutdown(struct platform_device *pdev) +{ + +} + +#if defined(CONFIG_OF) +static const struct of_device_id rk3036_tve_dt_ids[] = { + {.compatible = "rockchip,rk3036-tve",}, + {} +}; +#endif + +static struct platform_driver rk3036_tve_driver = { + .probe = rk3036_tve_probe, + .remove = NULL, + .driver = { + .name = "rk3036-tve", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rk3036_tve_dt_ids), + }, + .shutdown = rk3036_tve_shutdown, +}; + +static int __init rk3036_tve_init(void) +{ + return platform_driver_register(&rk3036_tve_driver); +} + +static void __exit rk3036_tve_exit(void) +{ + platform_driver_unregister(&rk3036_tve_driver); +} + +module_init(rk3036_tve_init); +module_exit(rk3036_tve_exit); + +/* Module information */ +MODULE_DESCRIPTION("ROCKCHIP RK3036 TV Encoder "); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/drivers/video/rockchip/tve/rk3036/rk3036_tve.h b/drivers/video/rockchip/tve/rk3036/rk3036_tve.h new file mode 100644 index 000000000000..d36573e0f5b0 --- /dev/null +++ b/drivers/video/rockchip/tve/rk3036/rk3036_tve.h @@ -0,0 +1,100 @@ +#ifndef __RK3036_TVE_H__ +#define __RK3036_TVE_H__ + +#define TV_CTRL (0x00) + #define m_CVBS_MODE (1 << 24) + #define m_CLK_UPSTREAM_EN (3 << 18) + #define m_TIMING_EN (3 << 16) + #define m_LUMA_FILTER_GAIN (3 << 9) + #define m_LUMA_FILTER_BW (1 << 8) + #define m_CSC_PATH (3 << 1) + + #define v_CVBS_MODE(x) ( (x & 1) << 24) + #define v_CLK_UPSTREAM_EN(x) ( (x & 3) << 18) + #define v_TIMING_EN(x) ( (x & 3) << 16) + #define v_LUMA_FILTER_GAIN(x) ( (x & 3) << 9) + #define v_LUMA_FILTER_UPSAMPLE(x) ( (x & 1) << 8) + #define v_CSC_PATH(x) ( (x & 3) << 1) + +#define TV_SYNC_TIMING (0x04) +#define TV_ACT_TIMING (0x08) +#define TV_ADJ_TIMING (0x0c) +#define TV_FREQ_SC (0x10) +#define TV_LUMA_FILTER0 (0x14) +#define TV_LUMA_FILTER1 (0x18) +#define TV_LUMA_FILTER2 (0x1C) +#define TV_ACT_ST (0x34) +#define TV_ROUTING (0x38) + #define m_DAC_SENSE_EN (1 << 27) + #define m_Y_IRE_7_5 (1 << 19) + #define m_Y_AGC_PULSE_ON (1 << 15) + #define m_Y_VIDEO_ON (1 << 11) + #define m_Y_SYNC_ON (1 << 7) + #define m_MONO_EN (1 << 2) + #define m_PIC_MODE (1 << 1) + + #define v_DAC_SENSE_EN(x) ( (x & 1) << 27) + #define v_Y_IRE_7_5(x) ( (x & 1) << 19) + #define v_Y_AGC_PULSE_ON(x) ( (x & 1) << 15) + #define v_Y_VIDEO_ON(x) ( (x & 1) << 11) + #define v_Y_SYNC_ON(x) ( (x & 1) << 7) + #define v_MONO_EN(x) ( (x & 1) << 2) + #define v_PIC_MODE(x) ( (x & 1) << 1) + +#define TV_SYNC_ADJUST (0x50) +#define TV_STATUS (0x54) +#define TV_RESET (0x68) + #define m_RESET (1 << 1); + #define v_RESET(x) ( (x & 1) << 1) +#define TV_SATURATION (0x78) +#define TV_BW_CTRL (0x8C) + #define m_CHROMA_BW (3 << 4) + #define m_COLOR_DIFF_BW (0xf) + + enum { + BP_FILTER_PASS = 0, + BP_FILTER_NTSC, + BP_FILTER_PAL, + }; + enum { + COLOR_DIFF_FILTER_OFF = 0, + COLOR_DIFF_FILTER_BW_0_6, + COLOR_DIFF_FILTER_BW_1_3, + COLOR_DIFF_FILTER_BW_2_0 + }; + + #define v_CHROMA_BW(x) ((3 & x) << 4) + #define v_COLOR_DIFF_BW(x) (0xF & x) + +#define TV_BRIGHTNESS_CONTRAST (0x90) + +#define m_EXTREF_EN (1 << 0) +#define m_VBG_EN (1 << 1) +#define m_DAC_EN (1 << 2) +#define m_SENSE_EN (1 << 3) +#define m_BIAS_EN (7 << 4) +#define m_DAC_GAIN (0x3f << 7) + +enum { + TVOUT_CVBS_NTSC = 0, + TVOUT_CVBS_PAL, +}; + +#define TVOUT_DEAULT TVOUT_CVBS_PAL + +#define grf_writel(offset, v) do{ writel_relaxed(v, RK_GRF_VIRT + offset);dsb();} while (0) + +struct rk3036_tve { + struct device *dev; + void __iomem *regbase; + u32 reg_phy_base; /* physical basic address of register*/ + u32 len; /* physical map length of register*/ + + struct rk_display_device *ddev; + unsigned int enable; + unsigned int suspend; + struct fb_videomode *mode; + struct list_head modelist; +}; + +#endif \ No newline at end of file diff --git a/drivers/video/rockchip/tve/rk610/Kconfig b/drivers/video/rockchip/tve/rk610/Kconfig new file mode 100644 index 000000000000..74eaf28651be --- /dev/null +++ b/drivers/video/rockchip/tve/rk610/Kconfig @@ -0,0 +1,14 @@ +config RK610_TVOUT + bool "RK610(Jetta) tvout support" + depends on MFD_RK610 + default n + help + Support Jetta(RK610) to output YPbPr and CVBS. + +config RK610_TVOUT_YPbPr + bool "support YPbPr output" + depends on RK610_TVOUT + +config RK610_TVOUT_CVBS + bool "support CVBS output" + depends on RK610_TVOUT diff --git a/drivers/video/rockchip/tve/rk610/Makefile b/drivers/video/rockchip/tve/rk610/Makefile new file mode 100644 index 000000000000..b7d457326d26 --- /dev/null +++ b/drivers/video/rockchip/tve/rk610/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the jetta tv control. +# +obj-$(CONFIG_RK610_TVOUT) += rk610_tv.o +obj-$(CONFIG_RK610_TVOUT_YPbPr) += rk610_tv_ypbpr.o +obj-$(CONFIG_RK610_TVOUT_CVBS) += rk610_tv_cvbs.o \ No newline at end of file diff --git a/drivers/video/rockchip/tve/rk610_tv.c b/drivers/video/rockchip/tve/rk610/rk610_tv.c similarity index 100% rename from drivers/video/rockchip/tve/rk610_tv.c rename to drivers/video/rockchip/tve/rk610/rk610_tv.c diff --git a/drivers/video/rockchip/tve/rk610_tv.h b/drivers/video/rockchip/tve/rk610/rk610_tv.h similarity index 100% rename from drivers/video/rockchip/tve/rk610_tv.h rename to drivers/video/rockchip/tve/rk610/rk610_tv.h diff --git a/drivers/video/rockchip/tve/rk610_tv_cvbs.c b/drivers/video/rockchip/tve/rk610/rk610_tv_cvbs.c similarity index 100% rename from drivers/video/rockchip/tve/rk610_tv_cvbs.c rename to drivers/video/rockchip/tve/rk610/rk610_tv_cvbs.c diff --git a/drivers/video/rockchip/tve/rk610_tv_ypbpr.c b/drivers/video/rockchip/tve/rk610/rk610_tv_ypbpr.c similarity index 100% rename from drivers/video/rockchip/tve/rk610_tv_ypbpr.c rename to drivers/video/rockchip/tve/rk610/rk610_tv_ypbpr.c diff --git a/include/linux/rk_fb.h b/include/linux/rk_fb.h index 37da53946f80..da1e03cae33a 100755 --- a/include/linux/rk_fb.h +++ b/include/linux/rk_fb.h @@ -342,6 +342,7 @@ struct rk_lcdc_win_area{ struct rk_lcdc_win { char name[5]; int id; + u32 logicalstate; bool state; /*on or off*/ bool last_state; /*on or off*/ u32 pseudo_pal[16]; @@ -546,8 +547,11 @@ struct rk_lcdc_driver { char fb1_win_id; char fb2_win_id; char fb3_win_id; + char mmu_dts_name[40]; + struct device *mmu_dev; int iommu_enabled; + struct rk_fb_reg_area_data reg_area_data; struct mutex fb_win_id_mutex; @@ -576,7 +580,7 @@ struct rk_lcdc_driver { #ifdef CONFIG_DRM_ROCKCHIP void (*irq_call_back)(struct rk_lcdc_driver *driver); #endif - + struct overscan overscan; }; /*disp_mode: dual display mode -- 2.34.1