Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 72a3ffb6 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: mdss: Add support for dynamic mode switch"

parents c1dda9bc c0407f8a
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -328,7 +328,15 @@ Optional properties:
					"reg_read" = Reads panel status register to check the panel status
- qcom,mdss-dsi-panel-status-value:	Specifies the value of the panel status register when the panel is
					in good state.

- qcom,dynamic-mode-switch-enabled:		Boolean used to mention whether panel supports
					dynamic switching from video mode to command mode
					and vice versa.
- qcom,video-to-cmd-mode-switch-commands:	List of commands that need to be sent
					to panel in order to switch from video mode to command mode dynamically.
					Refer to "qcom,mdss-dsi-on-command" section for adding commands.
- qcom,cmd-to-video-mode-switch-commands:	List of commands that need to be sent
					to panel in order to switch from command mode to video mode dynamically.
					Refer to "qcom,mdss-dsi-on-command" section for adding commands.
Note, if a given optional qcom,* binding is not present, then the driver will configure
the default values specified.

@@ -439,5 +447,9 @@ Example:
		qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
		qcom,mdss-dsi-panel-status-check-mode = "reg_read";
		qcom,mdss-dsi-panel-status-value = <0x9c>;
		qcom,dynamic-mode-switch-enabled;
		qcom,video-to-cmd-mode-switch-commands = [15 01 00 00 00 00 02 C2 0B
						15 01 00 00 00 00 02 C2 08];
		qcom,cmd-to-video-mode-switch-commands = [15 01 00 00 00 00 02 C2 03];
	};
};
+63 −8
Original line number Diff line number Diff line
@@ -76,6 +76,17 @@ static int mdss_dsi_panel_power_on(struct mdss_panel_data *pdata, int enable)
				panel_data);
	pr_debug("%s: enable=%d\n", __func__, enable);

	if (pdata->panel_info.dynamic_switch_pending) {
		/*
		 * Current implementation of dynamic mode switch
		 * relies on the GDSC to be disabled while switching.
		 */
		msm_dss_enable_vreg(
			ctrl_pdata->power_data[DSI_CORE_PM].vreg_config,
			ctrl_pdata->power_data[DSI_CORE_PM].num_vreg, enable);
		return 0;
	}

	if (enable) {
		for (i = 0; i < DSI_MAX_PM; i++) {
			ret = msm_dss_enable_vreg(
@@ -655,6 +666,31 @@ error:
	return ret;
}

static int mdss_dsi_update_panel_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata,
				int mode)
{
	int ret = 0;
	struct mdss_panel_info *pinfo = &(ctrl_pdata->panel_data.panel_info);

	if (mode == DSI_CMD_MODE) {
		pinfo->mipi.mode = DSI_CMD_MODE;
		pinfo->type = MIPI_CMD_PANEL;
		pinfo->mipi.vsync_enable = 1;
		pinfo->mipi.hw_vsync_mode = 1;
	} else {	/*video mode*/
		pinfo->mipi.mode = DSI_VIDEO_MODE;
		pinfo->type = MIPI_VIDEO_PANEL;
		pinfo->mipi.vsync_enable = 0;
		pinfo->mipi.hw_vsync_mode = 0;
	}

	ctrl_pdata->panel_mode = pinfo->mipi.mode;
	mdss_panel_get_dst_fmt(pinfo->bpp, pinfo->mipi.mode,
			pinfo->mipi.pixel_packing, &(pinfo->mipi.dst_format));
	pinfo->cont_splash_enabled = 0;

	return ret;
}
static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl,
	int enable)
{
@@ -885,12 +921,14 @@ static int mdss_dsi_unblank(struct mdss_panel_data *pdata)
	mipi  = &pdata->panel_info.mipi;

	if (!(ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT)) {
		if (!pdata->panel_info.dynamic_switch_pending) {
			ret = ctrl_pdata->on(pdata);
			if (ret) {
				pr_err("%s: unable to initialize the panel\n",
							__func__);
				return ret;
			}
		}
		ctrl_pdata->ctrl_state |= CTRL_STATE_PANEL_INIT;
	}

@@ -939,16 +977,29 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata)

	mdss_dsi_op_mode_config(DSI_CMD_MODE, pdata);

	if (pdata->panel_info.dynamic_switch_pending) {
		pr_info("%s: switching to %s mode\n", __func__,
			(pdata->panel_info.mipi.mode ? "video" : "command"));
		if (pdata->panel_info.type == MIPI_CMD_PANEL) {
			ctrl_pdata->switch_mode(pdata, DSI_VIDEO_MODE);
		} else if (pdata->panel_info.type == MIPI_VIDEO_PANEL) {
			ctrl_pdata->switch_mode(pdata, DSI_CMD_MODE);
			mdss_dsi_set_tear_off(ctrl_pdata);
		}
	}

	if ((pdata->panel_info.type == MIPI_CMD_PANEL) &&
		mipi->vsync_enable && mipi->hw_vsync_mode)
		mdss_dsi_set_tear_off(ctrl_pdata);

	if (ctrl_pdata->ctrl_state & CTRL_STATE_PANEL_INIT) {
		if (!pdata->panel_info.dynamic_switch_pending) {
			ret = ctrl_pdata->off(pdata);
			if (ret) {
				pr_err("%s: Panel OFF failed\n", __func__);
				return ret;
			}
		}
		ctrl_pdata->ctrl_state &= ~CTRL_STATE_PANEL_INIT;
	}

@@ -1180,6 +1231,10 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
	case MDSS_EVENT_DSI_ULPS_CTRL:
		rc = mdss_dsi_ulps_config(ctrl_pdata, (int)(unsigned long) arg);
		break;
	case MDSS_EVENT_DSI_DYNAMIC_SWITCH:
		rc = mdss_dsi_update_panel_config(ctrl_pdata,
					(int)(unsigned long) arg);
		break;
	default:
		pr_debug("%s: unhandled event=%d\n", __func__, event);
		break;
+6 −0
Original line number Diff line number Diff line
@@ -257,6 +257,7 @@ struct mdss_dsi_ctrl_pdata {
	int (*partial_update_fnc) (struct mdss_panel_data *pdata);
	int (*check_status) (struct mdss_dsi_ctrl_pdata *pdata);
	int (*cmdlist_commit)(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp);
	void (*switch_mode) (struct mdss_panel_data *pdata, int mode);
	struct mdss_panel_data panel_data;
	unsigned char *ctrl_base;
	struct dss_io_data ctrl_io;
@@ -301,6 +302,9 @@ struct mdss_dsi_ctrl_pdata {
	struct dsi_panel_cmds status_cmds;
	u32 status_value;

	struct dsi_panel_cmds video2cmd;
	struct dsi_panel_cmds cmd2video;

	struct dcs_cmd_list cmdlist;
	struct completion dma_comp;
	struct completion mdp_comp;
@@ -385,6 +389,8 @@ bool __mdss_dsi_clk_enabled(struct mdss_dsi_ctrl_pdata *ctrl, u8 clk_type);
int mdss_dsi_panel_init(struct device_node *node,
		struct mdss_dsi_ctrl_pdata *ctrl_pdata,
		bool cmd_cfg_cont_splash);
int mdss_panel_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing,
				char *dst_format);

static inline const char *__mdss_dsi_pm_name(enum dsi_pm_type module)
{
+69 −9
Original line number Diff line number Diff line
@@ -336,6 +336,36 @@ static int mdss_dsi_panel_partial_update(struct mdss_panel_data *pdata)
	return rc;
}

static void mdss_dsi_panel_switch_mode(struct mdss_panel_data *pdata,
							int mode)
{
	struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL;
	struct mipi_panel_info *mipi;
	struct dsi_panel_cmds *pcmds;

	if (pdata == NULL) {
		pr_err("%s: Invalid input data\n", __func__);
		return;
	}

	mipi  = &pdata->panel_info.mipi;

	if (!mipi->dynamic_switch_enabled)
		return;

	ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata,
				panel_data);

	if (mode == DSI_CMD_MODE)
		pcmds = &ctrl_pdata->video2cmd;
	else
		pcmds = &ctrl_pdata->cmd2video;

	mdss_dsi_panel_cmds_send(ctrl_pdata, pcmds);

	return;
}

static void mdss_dsi_panel_bl_ctrl(struct mdss_panel_data *pdata,
							u32 bl_level)
{
@@ -543,11 +573,16 @@ static int mdss_dsi_parse_dcs_cmds(struct device_node *np,
		len -= dchdr->dlen;
	}

	/*Set default link state to LP Mode*/
	pcmds->link_state = DSI_LP_MODE;

	if (link_key) {
		data = of_get_property(np, link_key, NULL);
		if (data && !strcmp(data, "dsi_hs_mode"))
			pcmds->link_state = DSI_HS_MODE;
		else
			pcmds->link_state = DSI_LP_MODE;
	}

	pr_debug("%s: dcs_cmd=%x len=%d, cmd_cnt=%d link_state=%d\n", __func__,
		pcmds->buf[0], pcmds->blen, pcmds->cmd_cnt, pcmds->link_state);
@@ -560,7 +595,7 @@ exit_free:
}


static int mdss_panel_dt_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing,
int mdss_panel_get_dst_fmt(u32 bpp, char mipi_mode, u32 pixel_packing,
				char *dst_format)
{
	int rc = 0;
@@ -809,6 +844,25 @@ static int mdss_dsi_parse_panel_features(struct device_node *np,
	pinfo->esd_check_enabled = of_property_read_bool(np,
		"qcom,esd-check-enabled");

	pinfo->mipi.dynamic_switch_enabled = of_property_read_bool(np,
		"qcom,dynamic-mode-switch-enabled");

	if (pinfo->mipi.dynamic_switch_enabled) {
		mdss_dsi_parse_dcs_cmds(np, &ctrl->video2cmd,
			"qcom,video-to-cmd-mode-switch-commands", NULL);

		mdss_dsi_parse_dcs_cmds(np, &ctrl->cmd2video,
			"qcom,cmd-to-video-mode-switch-commands", NULL);

		if (!ctrl->video2cmd.cmd_cnt || !ctrl->cmd2video.cmd_cnt) {
			pr_warn("No commands specified for dynamic switch\n");
			pinfo->mipi.dynamic_switch_enabled = 0;
		}
	}

	pr_info("%s: dynamic switch feature enabled: %d", __func__,
		pinfo->mipi.dynamic_switch_enabled);

	return 0;
}

@@ -866,9 +920,11 @@ static int mdss_panel_parse_dt(struct device_node *np,
	tmp = 0;
	data = of_get_property(np, "qcom,mdss-dsi-pixel-packing", NULL);
	if (data && !strcmp(data, "loose"))
		tmp = 1;
	rc = mdss_panel_dt_get_dst_fmt(pinfo->bpp,
		pinfo->mipi.mode, tmp,
		pinfo->mipi.pixel_packing = 1;
	else
		pinfo->mipi.pixel_packing = 0;
	rc = mdss_panel_get_dst_fmt(pinfo->bpp,
		pinfo->mipi.mode, pinfo->mipi.pixel_packing,
		&(pinfo->mipi.dst_format));
	if (rc) {
		pr_debug("%s: problem determining dst format. Set Default\n",
@@ -1160,9 +1216,13 @@ int mdss_dsi_panel_init(struct device_node *node,
	pr_info("%s: Continuous splash %s", __func__,
		pinfo->cont_splash_enabled ? "enabled" : "disabled");

	pinfo->dynamic_switch_pending = false;
	pinfo->is_lpm_mode = false;

	ctrl_pdata->on = mdss_dsi_panel_on;
	ctrl_pdata->off = mdss_dsi_panel_off;
	ctrl_pdata->panel_data.set_backlight = mdss_dsi_panel_bl_ctrl;
	ctrl_pdata->switch_mode = mdss_dsi_panel_switch_mode;

	return 0;
}
+124 −8
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ static int __mdss_fb_display_thread(void *data);
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);
void mdss_fb_no_update_notify_timer_cb(unsigned long data)
{
	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
@@ -439,6 +440,90 @@ static ssize_t mdss_fb_get_panel_info(struct device *dev,

	return ret;
}
/*
 * mdss_fb_lpm_enable() - Function to Control LowPowerMode
 * @mfd:	Framebuffer data structure for display
 * @mode:	Enabled/Disable LowPowerMode
 *		1: Enter into LowPowerMode
 *		0: Exit from LowPowerMode
 *
 * This Function dynamically switches to and from LowPowerMode
 * based on the argument @mode.
 */
static int mdss_fb_lpm_enable(struct msm_fb_data_type *mfd, int mode)
{
	int ret = 0;
	u32 bl_lvl = 0;
	struct mdss_panel_info *pinfo = NULL;
	struct mdss_panel_data *pdata;

	if (!mfd || !mfd->panel_info)
		return -EINVAL;

	pinfo = mfd->panel_info;

	if (!pinfo->mipi.dynamic_switch_enabled) {
		pr_warn("Panel does not support dynamic switch!\n");
		return 0;
	}

	if (mode == pinfo->mipi.mode) {
		pr_debug("Already in requested mode!\n");
		return 0;
	}

	pdata = dev_get_platdata(&mfd->pdev->dev);

	pr_debug("Enter mode: %d\n", mode);
	pdata->panel_info.dynamic_switch_pending = true;

	mutex_lock(&mfd->bl_lock);
	bl_lvl = mfd->bl_level;
	mdss_fb_set_backlight(mfd, 0);

	lock_fb_info(mfd->fbi);
	ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi,
						mfd->op_enable);
	if (ret) {
		pr_err("can't turn off display!\n");
		unlock_fb_info(mfd->fbi);
		mutex_unlock(&mfd->bl_lock);
		return ret;
	}

	mfd->op_enable = false;

	ret = mfd->mdp.configure_panel(mfd, mode);
	mdss_fb_set_mdp_sync_pt_threshold(mfd);

	mfd->op_enable = true;

	ret = mdss_fb_blank_sub(FB_BLANK_UNBLANK, mfd->fbi,
					mfd->op_enable);
	if (ret) {
		pr_err("can't turn on display!\n");
		unlock_fb_info(mfd->fbi);
		mutex_unlock(&mfd->bl_lock);
		return ret;
	}
	unlock_fb_info(mfd->fbi);

	mfd->bl_updated = true;
	mdss_fb_set_backlight(mfd, bl_lvl);
	mutex_unlock(&mfd->bl_lock);

	pdata->panel_info.dynamic_switch_pending = false;
	pdata->panel_info.is_lpm_mode = mode ? 1 : 0;

	if (ret) {
		pr_err("can't turn on display!\n");
		return ret;
	}

	pr_debug("Exit mode: %d\n", mode);

	return 0;
}

static DEVICE_ATTR(msm_fb_type, S_IRUGO, mdss_fb_get_type, NULL);
static DEVICE_ATTR(msm_fb_split, S_IRUGO | S_IWUSR, mdss_fb_show_split,
@@ -584,6 +669,21 @@ static int mdss_fb_probe(struct platform_device *pdev)
			__mdss_fb_sync_buf_done_callback;
	}

	mdss_fb_set_mdp_sync_pt_threshold(mfd);

	if (mfd->mdp.splash_init_fnc)
		mfd->mdp.splash_init_fnc(mfd);

	INIT_DELAYED_WORK(&mfd->idle_notify_work, __mdss_fb_idle_notify_work);

	return rc;
}

static void mdss_fb_set_mdp_sync_pt_threshold(struct msm_fb_data_type *mfd)
{
	if (!mfd)
		return;

	switch (mfd->panel.type) {
	case WRITEBACK_PANEL:
		mfd->mdp_sync_pt_data.threshold = 1;
@@ -598,15 +698,7 @@ static int mdss_fb_probe(struct platform_device *pdev)
		mfd->mdp_sync_pt_data.retire_threshold = 0;
		break;
	}

	if (mfd->mdp.splash_init_fnc)
		mfd->mdp.splash_init_fnc(mfd);

	INIT_DELAYED_WORK(&mfd->idle_notify_work, __mdss_fb_idle_notify_work);

	return rc;
}

static int mdss_fb_remove(struct platform_device *pdev)
{
	struct msm_fb_data_type *mfd;
@@ -967,6 +1059,7 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info,

static int mdss_fb_blank(int blank_mode, struct fb_info *info)
{
	struct mdss_panel_data *pdata;
	struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;

	mdss_fb_pan_idle(mfd);
@@ -977,6 +1070,18 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info)
			mfd->suspend.panel_power_on = false;
		return 0;
	}
	pr_debug("mode: %d\n", blank_mode);

	pdata = dev_get_platdata(&mfd->pdev->dev);

	if (pdata->panel_info.is_lpm_mode &&
			blank_mode == FB_BLANK_UNBLANK) {
		pr_debug("panel is in lpm mode\n");
		mfd->mdp.configure_panel(mfd, 0);
		mdss_fb_set_mdp_sync_pt_threshold(mfd);
		pdata->panel_info.is_lpm_mode = false;
	}

	return mdss_fb_blank_sub(blank_mode, info, mfd->op_enable);
}

@@ -2626,6 +2731,7 @@ int mdss_fb_do_ioctl(struct fb_info *info, unsigned int cmd,
	int ret = -ENOSYS;
	struct mdp_buf_sync buf_sync;
	struct msm_sync_pt_data *sync_pt_data = NULL;
	unsigned int dsi_mode = 0;

	if (!info || !info->par)
		return -EINVAL;
@@ -2692,6 +2798,16 @@ int mdss_fb_do_ioctl(struct fb_info *info, unsigned int cmd,
		ret = mdss_fb_display_commit(info, argp);
		break;

	case MSMFB_LPM_ENABLE:
		ret = copy_from_user(&dsi_mode, argp, sizeof(dsi_mode));
		if (ret) {
			pr_err("%s: MSMFB_LPM_ENABLE ioctl failed\n", __func__);
			goto exit;
		}

		ret = mdss_fb_lpm_enable(mfd, dsi_mode);
		break;

	default:
		if (mfd->mdp.ioctl_handler)
			ret = mfd->mdp.ioctl_handler(mfd, cmd, argp);
Loading