From 0507b2f2e28d09776c113e885f2e863db2d5c39c Mon Sep 17 00:00:00 2001 From: Veera Sundaram Sankaran Date: Fri, 29 Aug 2014 14:03:48 -0700 Subject: [PATCH] msm: mdss: added support to change panel resolution dynamically Helpful in changing the panel resoultion on the fly with the help of debugfs nodes and when used along with simulated panels, it allows to test different panel configurations dynamically. Change-Id: I9cdf82e4fffd3dd618ee97601f169d72ab76a473 Signed-off-by: Veera Sundaram Sankaran [cip@codeaurora.org: Moved mdss_panel.c file location] Signed-off-by: Clarence Ip --- drivers/video/fbdev/msm/Makefile | 1 + drivers/video/fbdev/msm/mdss_dsi.c | 29 +++++ drivers/video/fbdev/msm/mdss_dsi.h | 1 + drivers/video/fbdev/msm/mdss_fb.c | 51 ++++++++ drivers/video/fbdev/msm/mdss_mdp_overlay.c | 8 +- drivers/video/fbdev/msm/mdss_panel.c | 140 +++++++++++++++++++++ drivers/video/fbdev/msm/mdss_panel.h | 26 ++++ drivers/video/fbdev/msm/msm_mdss_io_8974.c | 6 +- 8 files changed, 260 insertions(+), 2 deletions(-) create mode 100644 drivers/video/fbdev/msm/mdss_panel.c diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index 78a9e625b63d..264c0d7278f6 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -33,6 +33,7 @@ mdss-dsi-objs := mdss_dsi.o mdss_dsi_host.o mdss_dsi_cmd.o mdss_dsi_status.o mdss-dsi-objs += mdss_dsi_panel.o mdss-dsi-objs += msm_mdss_io_8974.o obj-$(CONFIG_FB_MSM_MDSS) += mdss-dsi.o +obj-$(CONFIG_FB_MSM_MDSS) += mdss_panel.o obj-$(CONFIG_FB_MSM_MDSS_EDP_PANEL) += mdss_edp.o obj-$(CONFIG_FB_MSM_MDSS_EDP_PANEL) += mdss_edp_aux.o diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index 771ee17e432c..4a0f323085db 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -1180,6 +1180,28 @@ int mdss_dsi_register_recovery_handler(struct mdss_dsi_ctrl_pdata *ctrl, return 0; } +static int mdss_dsi_clk_refresh(struct mdss_panel_data *pdata) +{ + struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; + int rc = 0; + + ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, + panel_data); + rc = mdss_dsi_clk_div_config(&pdata->panel_info, + pdata->panel_info.mipi.frame_rate); + if (rc) { + pr_err("%s: unable to initialize the clk dividers\n", + __func__); + return rc; + } + ctrl_pdata->refresh_clk_rate = false; + ctrl_pdata->pclk_rate = pdata->panel_info.mipi.dsi_pclk_rate; + ctrl_pdata->byte_clk_rate = pdata->panel_info.clk_rate / 8; + pr_debug("%s ctrl_pdata->byte_clk_rate=%d ctrl_pdata->pclk_rate=%d\n", + __func__, ctrl_pdata->byte_clk_rate, ctrl_pdata->pclk_rate); + return rc; +} + static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, int event, void *arg) { @@ -1198,12 +1220,19 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, MDSS_XLOG(event, arg, ctrl_pdata->ndx, 0x3333); switch (event) { + case MDSS_EVENT_CHECK_PARAMS: + pr_debug("%s:Entered Case MDSS_EVENT_CHECK_PARAMS\n", __func__); + ctrl_pdata->refresh_clk_rate = true; + break; case MDSS_EVENT_LINK_READY: rc = mdss_dsi_on(pdata); mdss_dsi_op_mode_config(pdata->panel_info.mipi.mode, pdata); break; case MDSS_EVENT_UNBLANK: + if (ctrl_pdata->refresh_clk_rate) + rc = mdss_dsi_clk_refresh(pdata); + if (ctrl_pdata->on_cmds.link_state == DSI_LP_MODE) rc = mdss_dsi_unblank(pdata); break; diff --git a/drivers/video/fbdev/msm/mdss_dsi.h b/drivers/video/fbdev/msm/mdss_dsi.h index 550a5ff60c95..8878663d7722 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.h +++ b/drivers/video/fbdev/msm/mdss_dsi.h @@ -342,6 +342,7 @@ struct mdss_dsi_ctrl_pdata { struct dsi_drv_cm_data shared_pdata; u32 pclk_rate; u32 byte_clk_rate; + bool refresh_clk_rate; /* flag to recalculate clk_rate */ struct dss_module_power power_data[DSI_MAX_PM]; u32 dsi_irq_mask; struct mdss_hw *dsi_hw; diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index 81f27d7fe04f..0ec25e5160b2 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -106,6 +106,8 @@ static int mdss_fb_pan_idle(struct msm_fb_data_type *mfd); static int mdss_fb_send_panel_event(struct msm_fb_data_type *mfd, int event, void *arg); static void mdss_fb_set_mdp_sync_pt_threshold(struct msm_fb_data_type *mfd); +static void mdss_panelinfo_to_fb_var(struct mdss_panel_info *pinfo, + struct fb_var_screeninfo *var); void mdss_fb_no_update_notify_timer_cb(unsigned long data) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data; @@ -1185,6 +1187,34 @@ static void mdss_fb_stop_disp_thread(struct msm_fb_data_type *mfd) mfd->disp_thread = NULL; } +static void mdss_panel_validate_debugfs_info(struct msm_fb_data_type *mfd) +{ + struct mdss_panel_info *panel_info = mfd->panel_info; + struct fb_info *fbi = mfd->fbi; + struct fb_var_screeninfo *var = &fbi->var; + struct mdss_panel_data *pdata = container_of(panel_info, + struct mdss_panel_data, panel_info); + + if (panel_info->debugfs_info->override_flag) { + if (mfd->mdp.off_fnc) { + mfd->panel_reconfig = true; + mfd->mdp.off_fnc(mfd); + mfd->panel_reconfig = false; + } + + pr_debug("Overriding panel_info with debugfs_info\n"); + panel_info->debugfs_info->override_flag = 0; + mdss_panel_debugfsinfo_to_panelinfo(panel_info); + if (is_panel_split(mfd) && pdata->next) + mdss_fb_validate_split(pdata->panel_info.xres, + pdata->next->panel_info.xres, mfd); + mdss_panelinfo_to_fb_var(panel_info, var); + if (mdss_fb_send_panel_event(mfd, MDSS_EVENT_CHECK_PARAMS, + panel_info)) + pr_err("Failed to send panel event CHECK_PARAMS\n"); + } +} + static int mdss_fb_unblank_sub(struct msm_fb_data_type *mfd) { int ret = 0; @@ -1193,6 +1223,9 @@ static int mdss_fb_unblank_sub(struct msm_fb_data_type *mfd) if (!mfd) return -EINVAL; + if (mfd->panel_info->debugfs_info) + mdss_panel_validate_debugfs_info(mfd); + /* Start Display thread */ if (mfd->disp_thread == NULL) { ret = mdss_fb_start_disp_thread(mfd); @@ -1995,6 +2028,7 @@ static int mdss_fb_register(struct msm_fb_data_type *mfd) return -EPERM; } + mdss_panel_debugfs_init(panel_info); pr_info("FrameBuffer[%d] %dx%d registered successfully!\n", mfd->index, fbi->var.xres, fbi->var.yres); @@ -2625,6 +2659,23 @@ static void mdss_fb_var_to_panelinfo(struct fb_var_screeninfo *var, pinfo->clk_rate = var->pixclock; } +static void mdss_panelinfo_to_fb_var(struct mdss_panel_info *pinfo, + struct fb_var_screeninfo *var) +{ + struct mdss_panel_data *pdata = container_of(pinfo, + struct mdss_panel_data, panel_info); + + var->xres = mdss_fb_get_panel_xres(&pdata->panel_info); + var->yres = pinfo->yres; + var->lower_margin = pinfo->lcdc.v_front_porch; + var->upper_margin = pinfo->lcdc.v_back_porch; + var->vsync_len = pinfo->lcdc.v_pulse_width; + var->right_margin = pinfo->lcdc.h_front_porch; + var->left_margin = pinfo->lcdc.h_back_porch; + var->hsync_len = pinfo->lcdc.h_pulse_width; + var->pixclock = pinfo->clk_rate; +} + /** * __mdss_fb_perform_commit() - process a frame to display * @mfd: Framebuffer data structure for display diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 9747b5b65cc0..827c8dac9026 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -3464,8 +3464,14 @@ static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd) return -ENODEV; } - if (!mdss_mdp_ctl_is_power_on(mdp5_data->ctl)) + if (!mdss_mdp_ctl_is_power_on(mdp5_data->ctl)) { + if (mfd->panel_reconfig) { + mdp5_data->borderfill_enable = false; + mdss_mdp_ctl_destroy(mdp5_data->ctl); + mdp5_data->ctl = NULL; + } return 0; + } /* * Keep a reference to the runtime pm until the overlay is turned diff --git a/drivers/video/fbdev/msm/mdss_panel.c b/drivers/video/fbdev/msm/mdss_panel.c new file mode 100644 index 000000000000..ad4402821cbc --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_panel.c @@ -0,0 +1,140 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include + +#include "mdss_panel.h" + +#define NUM_DSI_INTF 2 + +int mdss_panel_debugfs_setup(struct mdss_panel_info *panel_info, struct dentry + *parent, char *dsi_str) +{ + struct mdss_panel_debugfs_info *debugfs_info; + debugfs_info = kzalloc(sizeof(*debugfs_info), GFP_KERNEL); + if (!debugfs_info) { + pr_err("No memory to create panel debugfs info"); + return -ENOMEM; + } + + debugfs_info->root = debugfs_create_dir(dsi_str, parent); + if (IS_ERR_OR_NULL(debugfs_info->root)) { + pr_err("Debugfs create dir failed with error: %ld\n", + PTR_ERR(debugfs_info->root)); + return -ENODEV; + } + + debugfs_create_u32("override_flag", 0644, parent, + (u32 *)&debugfs_info->override_flag); + + debugfs_create_u32("xres", 0644, debugfs_info->root, + (u32 *)&debugfs_info->xres); + debugfs_create_u32("yres", 0644, debugfs_info->root, + (u32 *)&debugfs_info->yres); + + debugfs_create_u32("h_back_porch", 0644, debugfs_info->root, + (u32 *)&debugfs_info->lcdc.h_back_porch); + debugfs_create_u32("h_front_porch", 0644, debugfs_info->root, + (u32 *)&debugfs_info->lcdc.h_front_porch); + debugfs_create_u32("h_pulse_width", 0644, debugfs_info->root, + (u32 *)&debugfs_info->lcdc.h_pulse_width); + + debugfs_create_u32("v_back_porch", 0644, debugfs_info->root, + (u32 *)&debugfs_info->lcdc.v_back_porch); + debugfs_create_u32("v_front_porch", 0644, debugfs_info->root, + (u32 *)&debugfs_info->lcdc.v_front_porch); + debugfs_create_u32("v_pulse_width", 0644, debugfs_info->root, + (u32 *)&debugfs_info->lcdc.v_pulse_width); + + debugfs_create_u32("frame_rate", 0644, parent, + (u32 *)&debugfs_info->frame_rate); + + debugfs_info->xres = panel_info->xres; + debugfs_info->yres = panel_info->yres; + debugfs_info->lcdc = panel_info->lcdc; + debugfs_info->frame_rate = panel_info->mipi.frame_rate; + debugfs_info->override_flag = 0; + + panel_info->debugfs_info = debugfs_info; + return 0; +} + +int mdss_panel_debugfs_init(struct mdss_panel_info *panel_info) +{ + struct mdss_panel_data *pdata; + struct dentry *parent; + char dsi_str[10]; + int dsi_index = 0; + int rc = 0; + + if (panel_info->type != MIPI_VIDEO_PANEL + && panel_info->type != MIPI_CMD_PANEL) + return -ENOTSUPP; + + pdata = container_of(panel_info, struct mdss_panel_data, panel_info); + parent = debugfs_create_dir("mdss_panel", NULL); + if (IS_ERR_OR_NULL(parent)) { + pr_err("Debugfs create dir failed with error: %ld\n", + PTR_ERR(parent)); + return -ENODEV; + } + + do { + snprintf(dsi_str, sizeof(dsi_str), "dsi%d", dsi_index++); + rc = mdss_panel_debugfs_setup(&pdata->panel_info, parent, + dsi_str); + if (rc) { + pr_err("error in initilizing panel debugfs\n"); + return rc; + } + pdata = pdata->next; + } while (pdata && dsi_index < NUM_DSI_INTF); + + pr_debug("Initilized mdss_panel_debugfs_info\n"); + return 0; +} + +void mdss_panel_debugfs_cleanup(struct mdss_panel_info *panel_info) +{ + struct mdss_panel_data *pdata; + struct mdss_panel_debugfs_info *debugfs_info; + pdata = container_of(panel_info, struct mdss_panel_data, panel_info); + do { + debugfs_info = pdata->panel_info.debugfs_info; + if (debugfs_info && debugfs_info->root) + debugfs_remove_recursive(debugfs_info->root); + pdata = pdata->next; + } while (pdata); + pr_debug("Cleaned up mdss_panel_debugfs_info\n"); +} + +void mdss_panel_debugfsinfo_to_panelinfo(struct mdss_panel_info *panel_info) +{ + struct mdss_panel_data *pdata; + struct mdss_panel_info *pinfo; + pdata = container_of(panel_info, struct mdss_panel_data, panel_info); + + do { + pinfo = &pdata->panel_info; + pinfo->xres = pinfo->debugfs_info->xres; + pinfo->yres = pinfo->debugfs_info->yres; + pinfo->lcdc = pinfo->debugfs_info->lcdc; + pinfo->mipi.frame_rate = pinfo->debugfs_info->frame_rate; + pdata = pdata->next; + } while (pdata); +} diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index f90287c548d2..6994ffbcc4f0 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -17,6 +17,7 @@ #include #include #include +#include /* panel id type */ struct panel_id { @@ -414,6 +415,9 @@ struct mdss_panel_info { struct mipi_panel_info mipi; struct lvds_panel_info lvds; struct edp_panel_info edp; + + /* debugfs structure for the panel */ + struct mdss_panel_debugfs_info *debugfs_info; }; struct mdss_panel_data { @@ -438,6 +442,16 @@ struct mdss_panel_data { struct mdss_panel_data *next; }; +struct mdss_panel_debugfs_info { + struct dentry *root; + u32 xres; + u32 yres; + struct lcd_panel_info lcdc; + u32 override_flag; + char frame_rate; + struct mdss_panel_debugfs_info *next; +}; + /** * mdss_get_panel_framerate() - get panel frame rate based on panel information * @panel_info: Pointer to panel info containing all panel information @@ -642,4 +656,16 @@ int mdss_panel_get_boot_cfg(void); * returns true if mdss is ready, else returns false. */ bool mdss_is_ready(void); +#ifdef CONFIG_FB_MSM_MDSS +int mdss_panel_debugfs_init(struct mdss_panel_info *panel_info); +void mdss_panel_debugfs_cleanup(struct mdss_panel_info *panel_info); +void mdss_panel_debugfsinfo_to_panelinfo(struct mdss_panel_info *panel_info); +#else +static inline int mdss_panel_debugfs_init( + struct mdss_panel_info *panel_info) { return 0; }; +static inline void mdss_panel_debugfs_cleanup( + struct mdss_panel_info *panel_info) { }; +static inline void mdss_panel_debugfsinfo_to_panelinfo( + struct mdss_panel_info *panel_info) { }; +#endif #endif /* MDSS_PANEL_H */ diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index 44f86563133d..fac282e8fa64 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -572,6 +572,10 @@ struct dsiphy_pll_divider_config pll_divider_config; int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, int frame_rate) { + struct mdss_panel_data *pdata = container_of(panel_info, + struct mdss_panel_data, panel_info); + struct mdss_dsi_ctrl_pdata *ctrl_pdata = container_of(pdata, + struct mdss_dsi_ctrl_pdata, panel_data); u32 fb_divider, rate, vco; u32 div_ratio = 0; u32 pll_analog_posDiv = 1; @@ -607,7 +611,7 @@ int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, h_period = mdss_panel_get_htotal(panel_info, true); v_period = mdss_panel_get_vtotal(panel_info); - if ((frame_rate != + if (ctrl_pdata->refresh_clk_rate || (frame_rate != panel_info->mipi.frame_rate) || (!panel_info->clk_rate)) { h_period += panel_info->lcdc.xres_pad;