From ab05f94b75a40fdd70f018a12fef1d8e0b972175 Mon Sep 17 00:00:00 2001 From: Ravishangar Kalyanam Date: Thu, 9 Feb 2012 19:18:45 -0800 Subject: [PATCH] msm_fb: display: Add LVDS display & LVDS/DSI panel auto detect support Add LVDS display PHY/PLL configuration and panel backlight support. Enable auto-detect support for LVDS/DSI panels Change-Id: I36a1a3c4cee9e015ae6fd03257bd10efa81450d8 Signed-off-by: Ravishangar Kalyanam --- drivers/video/msm/Kconfig | 8 ++ drivers/video/msm/lvds.c | 162 +++++++++++++++++++++++--- drivers/video/msm/lvds_chimei_wxga.c | 57 +++++++-- drivers/video/msm/mdp.c | 4 +- drivers/video/msm/mdp4.h | 7 +- drivers/video/msm/mdp4_overlay_lcdc.c | 70 ----------- 6 files changed, 210 insertions(+), 98 deletions(-) diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig index b590c411549..d3f8b2b3981 100644 --- a/drivers/video/msm/Kconfig +++ b/drivers/video/msm/Kconfig @@ -495,6 +495,14 @@ config FB_MSM_LCDC_MIPI_PANEL_AUTO_DETECT ---help--- Support for LCDC + MIPI panel auto detect +config FB_MSM_LVDS_MIPI_PANEL_DETECT + bool "LVDS + MIPI Panel Auto Detect" + select FB_MSM_MIPI_TOSHIBA_VIDEO_WSVGA_PT + select FB_MSM_LVDS_CHIMEI_WXGA + select FB_MSM_MIPI_CHIMEI_WXGA + ---help--- + Support for LVDS + MIPI panel auto detect + config FB_MSM_MDDI_PRISM_WVGA bool "MDDI Prism WVGA Panel" select FB_MSM_MDDI diff --git a/drivers/video/msm/lvds.c b/drivers/video/msm/lvds.c index 7ef437cbc0f..a69c64f5c40 100644 --- a/drivers/video/msm/lvds.c +++ b/drivers/video/msm/lvds.c @@ -19,9 +19,10 @@ #include #include #include -#include #include - +#include +#include +#include #include #include #include @@ -29,10 +30,9 @@ #include #include #include -#include #include "msm_fb.h" - +#include "mdp4.h" static int lvds_probe(struct platform_device *pdev); static int lvds_remove(struct platform_device *pdev); @@ -57,6 +57,126 @@ static struct platform_driver lvds_driver = { static struct lcdc_platform_data *lvds_pdata; +static void lvds_init(struct msm_fb_data_type *mfd) +{ + unsigned int lvds_intf, lvds_phy_cfg0; + + MDP_OUTP(MDP_BASE + 0xc2034, 0x33); + usleep(1000); + + /* LVDS PHY PLL configuration */ + MDP_OUTP(MDP_BASE + 0xc3000, 0x08); + MDP_OUTP(MDP_BASE + 0xc3004, 0x87); + MDP_OUTP(MDP_BASE + 0xc3008, 0x30); + MDP_OUTP(MDP_BASE + 0xc300c, 0x06); + MDP_OUTP(MDP_BASE + 0xc3014, 0x20); + MDP_OUTP(MDP_BASE + 0xc3018, 0x0F); + MDP_OUTP(MDP_BASE + 0xc301c, 0x01); + MDP_OUTP(MDP_BASE + 0xc3020, 0x41); + MDP_OUTP(MDP_BASE + 0xc3024, 0x0d); + MDP_OUTP(MDP_BASE + 0xc3028, 0x07); + MDP_OUTP(MDP_BASE + 0xc302c, 0x00); + MDP_OUTP(MDP_BASE + 0xc3030, 0x1c); + MDP_OUTP(MDP_BASE + 0xc3034, 0x01); + MDP_OUTP(MDP_BASE + 0xc3038, 0x00); + MDP_OUTP(MDP_BASE + 0xc3040, 0xC0); + MDP_OUTP(MDP_BASE + 0xc3044, 0x00); + MDP_OUTP(MDP_BASE + 0xc3048, 0x30); + MDP_OUTP(MDP_BASE + 0xc304c, 0x00); + + MDP_OUTP(MDP_BASE + 0xc3000, 0x11); + MDP_OUTP(MDP_BASE + 0xc3064, 0x05); + MDP_OUTP(MDP_BASE + 0xc3050, 0x20); + + MDP_OUTP(MDP_BASE + 0xc3000, 0x01); + /* Wait until LVDS PLL is locked and ready */ + while (!readl_relaxed(MDP_BASE + 0xc3080)) + cpu_relax(); + + writel_relaxed(0x00, mmss_cc_base + 0x0264); + writel_relaxed(0x00, mmss_cc_base + 0x0094); + + writel_relaxed(0x02, mmss_cc_base + 0x00E4); + + writel_relaxed((0x80 | readl_relaxed(mmss_cc_base + 0x00E4)), + mmss_cc_base + 0x00E4); + usleep(1000); + writel_relaxed((~0x80 & readl_relaxed(mmss_cc_base + 0x00E4)), + mmss_cc_base + 0x00E4); + + writel_relaxed(0x05, mmss_cc_base + 0x0094); + writel_relaxed(0x02, mmss_cc_base + 0x0264); + /* Wait until LVDS pixel clock output is enabled */ + mb(); + + if (mfd->panel_info.bpp == 24) { + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc2014, 0x03040508); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2018, 0x00000102); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc201c, 0x0c0d1011); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2020, 0x00090a0b); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc2024, 0x151a191a); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2028, 0x00121314); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc202c, 0x1706071b); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2030, 0x000e0f16); + + if (mfd->panel_info.lvds.channel_mode == + LVDS_DUAL_CHANNEL_MODE) { + lvds_intf = 0x0001ff80; + lvds_phy_cfg0 = BIT(6) | BIT(7); + if (mfd->panel_info.lvds.channel_swap) + lvds_intf |= BIT(4); + } else { + lvds_intf = 0x00010f84; + lvds_phy_cfg0 = BIT(6); + } + } else if (mfd->panel_info.bpp == 18) { + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc2014, 0x03040508); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2018, 0x00000102); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc201c, 0x0c0d1011); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2020, 0x00090a0b); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */ + MDP_OUTP(MDP_BASE + 0xc2024, 0x1518191a); + /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */ + MDP_OUTP(MDP_BASE + 0xc2028, 0x00121314); + + if (mfd->panel_info.lvds.channel_mode == + LVDS_DUAL_CHANNEL_MODE) { + lvds_intf = 0x00017788; + lvds_phy_cfg0 = BIT(6) | BIT(7); + if (mfd->panel_info.lvds.channel_swap) + lvds_intf |= BIT(4); + } else { + lvds_intf = 0x0001078c; + lvds_phy_cfg0 = BIT(6); + } + } + + /* MDP_LVDSPHY_CFG0 */ + MDP_OUTP(MDP_BASE + 0xc3100, lvds_phy_cfg0); + /* MDP_LCDC_LVDS_INTF_CTL */ + MDP_OUTP(MDP_BASE + 0xc2000, lvds_intf); + MDP_OUTP(MDP_BASE + 0xc3108, 0x30); + lvds_phy_cfg0 |= BIT(4); + + /* Wait until LVDS PHY registers are configured */ + mb(); + usleep(1); + /* MDP_LVDSPHY_CFG0, enable serialization */ + MDP_OUTP(MDP_BASE + 0xc3100, lvds_phy_cfg0); +} + static int lvds_off(struct platform_device *pdev) { int ret = 0; @@ -65,7 +185,8 @@ static int lvds_off(struct platform_device *pdev) mfd = platform_get_drvdata(pdev); ret = panel_next_off(pdev); - clk_disable(lvds_clk); + if (lvds_clk) + clk_disable(lvds_clk); if (lvds_pdata && lvds_pdata->lcdc_power_save) lvds_pdata->lcdc_power_save(0); @@ -97,22 +218,24 @@ static int lvds_on(struct platform_device *pdev) #endif mfd = platform_get_drvdata(pdev); - mfd->fbi->var.pixclock = clk_round_rate(lvds_clk, - mfd->fbi->var.pixclock); - ret = clk_set_rate(lvds_clk, mfd->fbi->var.pixclock); - if (ret) { - pr_err("%s: Can't set lvds clock to rate %u\n", - __func__, mfd->fbi->var.pixclock); - goto out; + if (lvds_clk) { + mfd->fbi->var.pixclock = clk_round_rate(lvds_clk, + mfd->fbi->var.pixclock); + ret = clk_set_rate(lvds_clk, mfd->fbi->var.pixclock); + if (ret) { + pr_err("%s: Can't set lvds clock to rate %u\n", + __func__, mfd->fbi->var.pixclock); + goto out; + } + clk_enable(lvds_clk); } - clk_enable(lvds_clk); - if (lvds_pdata && lvds_pdata->lcdc_power_save) lvds_pdata->lcdc_power_save(1); if (lvds_pdata && lvds_pdata->lcdc_gpio_config) ret = lvds_pdata->lcdc_gpio_config(1); + lvds_init(mfd); ret = panel_next_on(pdev); out: @@ -182,8 +305,11 @@ static int lvds_probe(struct platform_device *pdev) mfd->fb_imgType = MDP_RGB_565; fbi = mfd->fbi; - fbi->var.pixclock = clk_round_rate(lvds_clk, - mfd->panel_info.clk_rate); + if (lvds_clk) { + fbi->var.pixclock = clk_round_rate(lvds_clk, + mfd->panel_info.clk_rate); + } + fbi->var.left_margin = mfd->panel_info.lcdc.h_back_porch; fbi->var.right_margin = mfd->panel_info.lcdc.h_front_porch; fbi->var.upper_margin = mfd->panel_info.lcdc.v_back_porch; @@ -224,9 +350,9 @@ static int lvds_register_driver(void) static int __init lvds_driver_init(void) { lvds_clk = clk_get(NULL, "lvds_clk"); - if (IS_ERR(lvds_clk)) { + if (IS_ERR_OR_NULL(lvds_clk)) { pr_err("Couldnt find lvds_clk\n"); - return -EINVAL; + lvds_clk = NULL; } return lvds_register_driver(); diff --git a/drivers/video/msm/lvds_chimei_wxga.c b/drivers/video/msm/lvds_chimei_wxga.c index 2c6b6d4a38b..9a385b9235a 100644 --- a/drivers/video/msm/lvds_chimei_wxga.c +++ b/drivers/video/msm/lvds_chimei_wxga.c @@ -11,9 +11,19 @@ */ #include "msm_fb.h" +#include +#include -static struct msm_panel_common_pdata *cm_pdata; +#define LVDS_CHIMEI_PWM_FREQ_HZ 300 +#define LVDS_CHIMEI_PWM_PERIOD_USEC (USEC_PER_SEC / LVDS_CHIMEI_PWM_FREQ_HZ) +#define LVDS_CHIMEI_PWM_LEVEL 255 +#define LVDS_CHIMEI_PWM_DUTY_LEVEL \ + (LVDS_CHIMEI_PWM_PERIOD_USEC / LVDS_CHIMEI_PWM_LEVEL) + + +static struct lvds_panel_platform_data *cm_pdata; static struct platform_device *cm_fbpdev; +static struct pwm_device *bl_lpm; static int lvds_chimei_panel_on(struct platform_device *pdev) { @@ -27,6 +37,26 @@ static int lvds_chimei_panel_off(struct platform_device *pdev) static void lvds_chimei_set_backlight(struct msm_fb_data_type *mfd) { + int ret; + + pr_debug("%s: back light level %d\n", __func__, mfd->bl_level); + + if (bl_lpm) { + ret = pwm_config(bl_lpm, LVDS_CHIMEI_PWM_DUTY_LEVEL * + mfd->bl_level, LVDS_CHIMEI_PWM_PERIOD_USEC); + if (ret) { + pr_err("pwm_config on lpm failed %d\n", ret); + return; + } + if (mfd->bl_level) { + ret = pwm_enable(bl_lpm); + if (ret) + pr_err("pwm enable/disable on lpm failed" + "for bl %d\n", mfd->bl_level); + } else { + pwm_disable(bl_lpm); + } + } } static int __devinit lvds_chimei_probe(struct platform_device *pdev) @@ -40,6 +70,17 @@ static int __devinit lvds_chimei_probe(struct platform_device *pdev) return 0; } + if (cm_pdata != NULL) + bl_lpm = pwm_request(cm_pdata->gpio[0], + "backlight"); + + if (bl_lpm == NULL || IS_ERR(bl_lpm)) { + pr_err("%s pwm_request() failed\n", __func__); + bl_lpm = NULL; + } + pr_debug("bl_lpm = %p lpm = %d\n", bl_lpm, + cm_pdata->gpio[0]); + cm_fbpdev = msm_fb_add_device(pdev); if (!cm_fbpdev) { dev_err(&pdev->dev, "failed to add msm_fb device\n"); @@ -85,8 +126,8 @@ static int __init lvds_chimei_wxga_init(void) return ret; pinfo = &lvds_chimei_panel_data.panel_info; - pinfo->xres = 320; - pinfo->yres = 240; + pinfo->xres = 1366; + pinfo->yres = 768; MSM_FB_SINGLE_MODE_PANEL(pinfo); pinfo->type = LVDS_PANEL; pinfo->pdest = DISPLAY_1; @@ -94,7 +135,7 @@ static int __init lvds_chimei_wxga_init(void) pinfo->bpp = 24; pinfo->fb_num = 2; pinfo->clk_rate = 75000000; - pinfo->bl_max = 15; + pinfo->bl_max = 255; pinfo->bl_min = 1; /* @@ -107,12 +148,14 @@ static int __init lvds_chimei_wxga_init(void) pinfo->lcdc.v_back_porch = 0; pinfo->lcdc.v_front_porch = 38; pinfo->lcdc.v_pulse_width = 20; - pinfo->lcdc.border_clr = 0xffff00; pinfo->lcdc.underflow_clr = 0xff; pinfo->lcdc.hsync_skew = 0; pinfo->lvds.channel_mode = LVDS_SINGLE_CHANNEL_MODE; - pinfo->lcdc.xres_pad = 1046; - pinfo->lcdc.yres_pad = 528; + + /* Set border color, padding only for reducing active display region */ + pinfo->lcdc.border_clr = 0x0; + pinfo->lcdc.xres_pad = 0; + pinfo->lcdc.yres_pad = 0; ret = platform_device_register(&this_device); if (ret) diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c index 4c443ea33b4..43520e0a946 100644 --- a/drivers/video/msm/mdp.c +++ b/drivers/video/msm/mdp.c @@ -1221,7 +1221,7 @@ int mdp_bus_scale_update_request(uint32_t index) return -EINVAL; } if (mdp_bus_scale_handle < 1) { - printk(KERN_ERR "%s invalid bus handle\n", __func__); + pr_debug("%s invalid bus handle\n", __func__); return -EINVAL; } return msm_bus_scale_client_update_request(mdp_bus_scale_handle, @@ -1318,7 +1318,7 @@ static int mdp_irq_clk_setup(void) if (IS_ERR(mdp_pclk)) mdp_pclk = NULL; - if (mdp_rev == MDP_REV_42) { + if (mdp_rev >= MDP_REV_42) { mdp_lut_clk = clk_get(NULL, "lut_mdp"); if (IS_ERR(mdp_lut_clk)) { ret = PTR_ERR(mdp_lut_clk); diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h index 7b2b2c2d7a8..155bc543bb4 100644 --- a/drivers/video/msm/mdp4.h +++ b/drivers/video/msm/mdp4.h @@ -26,6 +26,7 @@ extern uint32 mdp_intr_mask; extern spinlock_t mdp_spin_lock; extern struct mdp4_statistic mdp4_stat; extern uint32 mdp4_extn_disp; +extern char *mmss_cc_base; /* mutimedia sub system clock control */ #define MDP4_OVERLAYPROC0_BASE 0x10000 #define MDP4_OVERLAYPROC1_BASE 0x18000 @@ -417,6 +418,7 @@ int mdp4_overlay_dtv_set(struct msm_fb_data_type *mfd, struct mdp4_overlay_pipe *pipe); int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd, struct mdp4_overlay_pipe *pipe); +void mdp4_dma_e_done_dtv(void); #else static inline void mdp4_overlay_dtv_ov_done_push(struct msm_fb_data_type *mfd, struct mdp4_overlay_pipe *pipe) @@ -438,6 +440,10 @@ static inline int mdp4_overlay_dtv_unset(struct msm_fb_data_type *mfd, { return 0; } +static inline void mdp4_dma_e_done_dtv(void) +{ + return; +} #endif #ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY @@ -691,7 +697,6 @@ void mdp4_writeback_kickoff_video(struct msm_fb_data_type *mfd, struct mdp4_overlay_pipe *pipe); void mdp4_writeback_dma_busy_wait(struct msm_fb_data_type *mfd); void mdp4_overlay1_done_writeback(struct mdp_dma_data *dma); -void mdp4_dma_e_done_dtv(void); int mdp4_writeback_start(struct fb_info *info); int mdp4_writeback_stop(struct fb_info *info); diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c index fdb1ec52bdc..3dece1f136b 100644 --- a/drivers/video/msm/mdp4_overlay_lcdc.c +++ b/drivers/video/msm/mdp4_overlay_lcdc.c @@ -45,73 +45,6 @@ int first_pixel_start_y; static struct mdp4_overlay_pipe *lcdc_pipe; static struct completion lcdc_comp; -static void lvds_init(struct msm_fb_data_type *mfd) -{ - unsigned int lvds_intf, lvds_phy_cfg0; - - if (mfd->panel_info.bpp == 24) { - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */ - MDP_OUTP(MDP_BASE + 0xc2014, 0x03040508); - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */ - MDP_OUTP(MDP_BASE + 0xc2018, 0x00000102); - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */ - MDP_OUTP(MDP_BASE + 0xc201c, 0x0c0d1011); - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */ - MDP_OUTP(MDP_BASE + 0xc2020, 0x00090a0b); - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */ - MDP_OUTP(MDP_BASE + 0xc2024, 0x1518191a); - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */ - MDP_OUTP(MDP_BASE + 0xc2028, 0x00121314); - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_3_TO_0 */ - MDP_OUTP(MDP_BASE + 0xc202c, 0x0f16171b); - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D3_6_TO_4 */ - MDP_OUTP(MDP_BASE + 0xc2030, 0x0006070e); - - if (mfd->panel_info.lvds.channel_mode == - LVDS_DUAL_CHANNEL_MODE) { - lvds_intf = 0x0001ff80; - lvds_phy_cfg0 = BIT(6) | BIT(7); - if (mfd->panel_info.lvds.channel_swap) - lvds_intf |= BIT(4); - } else { - lvds_intf = 0x00010f84; - lvds_phy_cfg0 = BIT(6); - } - } else if (mfd->panel_info.bpp == 18) { - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_3_TO_0 */ - MDP_OUTP(MDP_BASE + 0xc2014, 0x03040508); - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D0_6_TO_4 */ - MDP_OUTP(MDP_BASE + 0xc2018, 0x00000102); - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_3_TO_0 */ - MDP_OUTP(MDP_BASE + 0xc201c, 0x0c0d1011); - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D1_6_TO_4 */ - MDP_OUTP(MDP_BASE + 0xc2020, 0x00090a0b); - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_3_TO_0 */ - MDP_OUTP(MDP_BASE + 0xc2024, 0x1518191a); - /* MDP_LCDC_LVDS_MUX_CTL_FOR_D2_6_TO_4 */ - MDP_OUTP(MDP_BASE + 0xc2028, 0x00121314); - - if (mfd->panel_info.lvds.channel_mode == - LVDS_DUAL_CHANNEL_MODE) { - lvds_intf = 0x00017788; - lvds_phy_cfg0 = BIT(6) | BIT(7); - if (mfd->panel_info.lvds.channel_swap) - lvds_intf |= BIT(4); - } else { - lvds_intf = 0x0001078c; - lvds_phy_cfg0 = BIT(6); - } - } - - /* MDP_LVDSPHY_CFG0 */ - MDP_OUTP(MDP_BASE + 0xc3100, lvds_phy_cfg0); - /* MDP_LCDC_LVDS_INTF_CTL */ - MDP_OUTP(MDP_BASE + 0xc2000, lvds_intf); - lvds_phy_cfg0 |= BIT(4); - /* MDP_LVDSPHY_CFG0, enable serialization */ - MDP_OUTP(MDP_BASE + 0xc3100, lvds_phy_cfg0); -} - int mdp_lcdc_on(struct platform_device *pdev) { int lcdc_width; @@ -305,9 +238,6 @@ int mdp_lcdc_on(struct platform_device *pdev) #endif mdp_histogram_ctrl(TRUE); - if (mfd->panel.type == LVDS_PANEL) - lvds_init(mfd); - ret = panel_next_on(pdev); if (ret == 0) { /* enable LCDC block */