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

Commit 72fd1cec authored by Aravind Venkateswaran's avatar Aravind Venkateswaran Committed by Matt Wagantall
Browse files

msm: mdss: add support to configure DSI branch clock source



Based on the board configuration, the two DSI controllers can either
drive two independent panels or drive a single panel in split display
mode. DSI0 PLL can drive split display configuration ands it is not
supported by DSI1 PLL. DSI0 PLL can drive a single DSI panel on either
DSI0 or DSI1 controller whereas DSI1 PLL can drive a DSI panel only on
DSI1 controller. Based on this, it is necessary to configure the
source for the DSI link clocks dynamically. Implement this by adding
support for a new pll source configuration entry.

Change-Id: Ie57afbfe8cb14b80d0b52ed3825972ae34af27e2
Signed-off-by: default avatarKuogee Hsieh <khsieh@codeaurora.org>
Signed-off-by: default avatarAravind Venkateswaran <aravindh@codeaurora.org>
parent f0f54f28
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -31,6 +31,19 @@ Optional properties:
					-- qcom,supply-post-on-sleep: time to sleep (ms) after turning on
					-- qcom,supply-pre-off-sleep: time to sleep (ms) before turning off
					-- qcom,supply-post-off-sleep: time to sleep (ms) after turning off
- pll-src-config			Specified the source PLL for the DSI
					link clocks:
					"PLL0" - Clocks sourced out of DSI PLL0
					"PLL1" - Clocks sourced out of DSI PLL1
					This property is only valid for
					certain DSI hardware configurations
					mentioned in the "hw-config" binding above.
					For example, in split_dsi config, the clocks can
					only be sourced out of PLL0. For
					dual_dsi, both PLL would be active.
					For single DSI, it is possible to
					select either PLL. If no value is specified,
					the default value for single DSI is set as PLL0.
- qcom,mmss-ulp-clamp-ctrl-offset:	Specifies the offset for dsi ulps clamp control register.
- qcom,mmss-phyreset-ctrl-offset:	Specifies the offset for dsi phy reset control register.
- qcom,timing-db-mode:			Boolean specifies dsi timing mode registers are supported or not.
@@ -93,6 +106,7 @@ Example:
	mdss_dsi: qcom,mdss_dsi@0 {
		compatible = "qcom,mdss-dsi";
		hw-config = "single_dsi";
		pll-src-config = "PLL0";
		#address-cells = <1>;
		#size-cells = <1>;
		vdda-supply = <&pm8226_l4>;
+14 −2
Original line number Diff line number Diff line
@@ -282,9 +282,15 @@
		clocks = <&clock_mmss clk_mdss_mdp_vote_clk>,
			 <&clock_mmss clk_mdss_ahb_clk>,
			 <&clock_mmss clk_mmss_misc_ahb_clk>,
			 <&clock_mmss clk_mdss_axi_clk>;
			 <&clock_mmss clk_mdss_axi_clk>,
			 <&clock_mmss clk_ext_byte0_clk_src>,
			 <&clock_mmss clk_ext_byte1_clk_src>,
			 <&clock_mmss clk_ext_pclk0_clk_src>,
			 <&clock_mmss clk_ext_pclk1_clk_src>;
		clock-names = "mdp_core_clk", "iface_clk",
			"core_mmss_clk", "bus_clk";
			"core_mmss_clk", "bus_clk",
			"ext_byte0_clk", "ext_byte1_clk",
			"ext_pixel0_clk", "ext_pixel1_clk";

		qcom,core-supply-entries {
			#address-cells = <1>;
@@ -346,11 +352,14 @@
			clocks = <&clock_mmss clk_mdss_byte0_clk>,
				 <&clock_mmss clk_mdss_pclk0_clk>,
				 <&clock_mmss clk_mdss_esc0_clk>,
				 <&clock_mmss clk_byte0_clk_src>,
				 <&clock_mmss clk_pclk0_clk_src>,
				 <&mdss_dsi0_pll clk_dsi0pll_byte_clk_mux>,
				 <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_mux>,
				 <&mdss_dsi0_pll clk_dsi0pll_byte_clk_src>,
				 <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_src>;
			clock-names = "byte_clk", "pixel_clk", "core_clk",
				"byte_clk_rcg", "pixel_clk_rcg",
				"pll_byte_clk_mux", "pll_pixel_clk_mux",
				"pll_byte_clk_src", "pll_pixel_clk_src";

@@ -386,11 +395,14 @@
			clocks = <&clock_mmss clk_mdss_byte1_clk>,
				 <&clock_mmss clk_mdss_pclk1_clk>,
				 <&clock_mmss clk_mdss_esc1_clk>,
				 <&clock_mmss clk_byte1_clk_src>,
				 <&clock_mmss clk_pclk1_clk_src>,
				 <&mdss_dsi0_pll clk_dsi0pll_byte_clk_mux>,
				 <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_mux>,
				 <&mdss_dsi0_pll clk_dsi0pll_byte_clk_src>,
				 <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_src>;
			clock-names = "byte_clk", "pixel_clk", "core_clk",
				"byte_clk_rcg", "pixel_clk_rcg",
				"pll_byte_clk_mux", "pll_pixel_clk_mux",
				"pll_byte_clk_src", "pll_pixel_clk_src";

+2 −2
Original line number Diff line number Diff line
@@ -825,9 +825,9 @@
			 <&clock_gcc clk_gpll0_out_main>,
			 <&clock_gcc clk_gcc_mmss_gpll0_div_clk>,
			 <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_mux>,
			 <&mdss_dsi0_pll clk_dsi0pll_pixel_clk_mux>,
			 <&mdss_dsi0_pll clk_dsi0pll_byte_clk_mux>,
			 <&mdss_dsi1_pll clk_dsi1pll_pixel_clk_mux>,
			 <&mdss_dsi0_pll clk_dsi0pll_byte_clk_mux>,
			 <&mdss_dsi1_pll clk_dsi1pll_byte_clk_mux>,
			<&mdss_hdmi_pll clk_hdmi_vco_clk>;
		#clock-cells = <1>;
	};
+202 −11
Original line number Diff line number Diff line
@@ -48,6 +48,125 @@ static struct mdss_dsi_ctrl_pdata *mdss_dsi_get_ctrl(u32 ctrl_id)
	return mdss_dsi_res->ctrl_pdata[ctrl_id];
}

static void mdss_dsi_config_clk_src(struct platform_device *pdev)
{
	struct mdss_dsi_data *dsi_res = platform_get_drvdata(pdev);
	struct dsi_shared_data *sdata = dsi_res->shared_data;

	if (!sdata->ext_byte0_clk || !sdata->ext_byte1_clk ||
		!sdata->ext_pixel0_clk || !sdata->ext_pixel1_clk) {
		pr_debug("%s: config_clk_src not needed\n", __func__);
		return;
	}

	if (!mdss_dsi_is_hw_config_dual(sdata)) {
		/*
		 * For split-dsi and single-dsi use cases, map the PLL source
		 * based on the pll source configuration. It is possible that
		 * for split-dsi case, the only supported config is to source
		 * the clocks from PLL0. This is not explictly checked here as
		 * it should have been already enforced when validating the
		 * board configuration.
		 */
		if (mdss_dsi_is_pll_src_pll0(sdata)) {
			pr_debug("%s: single source: PLL0", __func__);
			sdata->byte0_parent = sdata->ext_byte0_clk;
			sdata->pixel0_parent = sdata->ext_pixel0_clk;
		} else {
			pr_debug("%s: single source: PLL1", __func__);
			sdata->byte0_parent = sdata->ext_byte1_clk;
			sdata->pixel0_parent = sdata->ext_pixel1_clk;
		}
		sdata->byte1_parent = sdata->byte0_parent;
		sdata->pixel1_parent = sdata->pixel0_parent;
	} else {
		/*
		 * For dual-dsi use cases, map:
		 *     DSI0 <--> PLL0
		 *     DSI1 <--> PLL1
		 */
		pr_debug("%s: dual-dsi: DSI0 <--> PLL0, DSI1 <--> PLL1",
			__func__);
		sdata->byte0_parent = sdata->ext_byte0_clk;
		sdata->byte1_parent = sdata->ext_byte1_clk;
		sdata->pixel0_parent = sdata->ext_pixel0_clk;
		sdata->pixel1_parent = sdata->ext_pixel1_clk;
	}

	return;
}

static char const *mdss_dsi_get_clk_src(struct mdss_dsi_ctrl_pdata *ctrl)
{
	struct dsi_shared_data *sdata;

	if (!ctrl) {
		pr_err("%s: Invalid input data\n", __func__);
		return "????";
	}

	sdata = ctrl->shared_data;

	if (mdss_dsi_is_left_ctrl(ctrl)) {
		if (sdata->byte0_parent == sdata->ext_byte0_clk)
			return "PLL0";
		else
			return "PLL1";
	} else {
		if (sdata->byte1_parent == sdata->ext_byte0_clk)
			return "PLL0";
		else
			return "PLL1";
	}
}

static int mdss_dsi_set_clk_src(struct mdss_dsi_ctrl_pdata *ctrl)
{
	int rc;
	struct dsi_shared_data *sdata;
	struct clk *byte_parent, *pixel_parent;

	if (!ctrl) {
		pr_err("%s: Invalid input data\n", __func__);
		return -EINVAL;
	}

	sdata = ctrl->shared_data;

	if (!ctrl->byte_clk_rcg || !ctrl->pixel_clk_rcg) {
		pr_debug("%s: set_clk_src not needed\n", __func__);
		return 0;
	}

	if (mdss_dsi_is_left_ctrl(ctrl)) {
		byte_parent = sdata->byte0_parent;
		pixel_parent = sdata->pixel0_parent;
	} else {
		byte_parent = sdata->byte1_parent;
		pixel_parent = sdata->pixel1_parent;
	}

	rc = clk_set_parent(ctrl->byte_clk_rcg, byte_parent);
	if (rc) {
		pr_err("%s: failed to set parent for byte clk for ctrl%d. rc=%d\n",
			__func__, ctrl->ndx, rc);
		goto error;
	}

	rc = clk_set_parent(ctrl->pixel_clk_rcg, pixel_parent);
	if (rc) {
		pr_err("%s: failed to set parent for pixel clk for ctrl%d. rc=%d\n",
			__func__, ctrl->ndx, rc);
		goto error;
	}

	pr_debug("%s: ctrl%d clock source set to %s", __func__, ctrl->ndx,
		mdss_dsi_get_clk_src(ctrl));

error:
	return rc;
}

static int mdss_dsi_regulator_init(struct platform_device *pdev,
		struct dsi_shared_data *sdata)
{
@@ -1079,10 +1198,16 @@ int mdss_dsi_on(struct mdss_panel_data *pdata)
	ret = mdss_dsi_panel_power_ctrl(pdata, MDSS_PANEL_POWER_ON);
	if (ret) {
		pr_err("%s:Panel power on failed. rc=%d\n", __func__, ret);
		return ret;
		goto end;
	}

	ret = mdss_dsi_set_clk_src(ctrl_pdata);
	if (ret) {
		pr_err("%s: failed to set clk src. rc=%d\n", __func__, ret);
		goto end;
	}

	if (cur_power_state != MDSS_PANEL_POWER_OFF) {
	if (mdss_panel_is_power_on(cur_power_state)) {
		pr_debug("%s: dsi_on from panel low power state\n", __func__);
		goto end;
	}
@@ -1123,7 +1248,7 @@ int mdss_dsi_on(struct mdss_panel_data *pdata)

end:
	pr_debug("%s-:\n", __func__);
	return 0;
	return ret;
}

static int mdss_dsi_pinctrl_set_state(
@@ -2220,7 +2345,7 @@ static void mdss_dsi_res_deinit(struct platform_device *pdev)
			&sdata->power_data[i]);
	}

	mdss_dsi_bus_clk_deinit(&pdev->dev, sdata);
	mdss_dsi_core_clk_deinit(&pdev->dev, sdata);

	if (sdata)
		devm_kfree(&pdev->dev, sdata);
@@ -2270,9 +2395,9 @@ static int mdss_dsi_res_init(struct platform_device *pdev)
			goto mem_fail;
		}

		rc = mdss_dsi_bus_clk_init(pdev, sdata);
		rc = mdss_dsi_core_clk_init(pdev, sdata);
		if (rc) {
			pr_err("%s: failed to initialize DSI Bus clocks\n",
			pr_err("%s: failed to initialize DSI core clocks\n",
				__func__);
			goto mem_fail;
		}
@@ -2369,6 +2494,65 @@ static int mdss_dsi_parse_hw_cfg(struct platform_device *pdev)
	return 0;
}

static void mdss_dsi_parse_pll_src_cfg(struct platform_device *pdev)
{
	const char *data;
	struct dsi_shared_data *sdata = mdss_dsi_res->shared_data;

	sdata->pll_src_config = PLL_SRC_0;
	data = of_get_property(pdev->dev.of_node, "pll-src-config", NULL);
	if (data) {
		if (!strcmp(data, "PLL0"))
			sdata->pll_src_config = PLL_SRC_0;
		else if (!strcmp(data, "PLL1"))
			sdata->pll_src_config = PLL_SRC_1;
		else
			pr_err("%s: invalid pll src config %s. Using PLL_SRC_0 as default\n",
				__func__, data);
	} else {
		pr_debug("%s: PLL src config not present. Using PLL0 by default\n",
			__func__);
	}

	pr_debug("%s: pll_src_config = %d", __func__, sdata->pll_src_config);

	return;
}

static int mdss_dsi_validate_pll_src_config(struct dsi_shared_data *sdata)
{
	int rc = 0;

	/*
	 * DSI PLL1 can only drive DSI PHY1. As such:
	 *     - For split dsi config, only PLL0 is supported
	 *     - For dual dsi config, DSI0-PLL0 and DSI1-PLL1 is the only
	 *       possible configuration
	 *     - For single dsi, it is not possible to source the clocks for
	 *       DSI0 from PLL1.
	 */

	if (mdss_dsi_is_hw_config_split(sdata) &&
		mdss_dsi_is_pll_src_pll1(sdata)) {
		pr_err("%s: unsupported PLL config: using PLL1 for split-dsi\n",
			__func__);
		rc = -EINVAL;
		goto error;
	}

	/* todo: enforce remaining checks */

error:
	return rc;
}

static int mdss_dsi_validate_config(struct platform_device *pdev)
{
	struct dsi_shared_data *sdata = mdss_dsi_res->shared_data;

	return mdss_dsi_validate_pll_src_config(sdata);
}

static const struct of_device_id mdss_dsi_ctrl_dt_match[] = {
	{.compatible = "qcom,mdss-dsi-ctrl"},
	{}
@@ -2419,10 +2603,21 @@ static int mdss_dsi_probe(struct platform_device *pdev)
		return rc;
	}

	mdss_dsi_parse_pll_src_cfg(pdev);

	of_platform_populate(pdev->dev.of_node, mdss_dsi_ctrl_dt_match,
				NULL, &pdev->dev);

	return 0;
	rc = mdss_dsi_validate_config(pdev);
	if (rc) {
		pr_err("%s: Invalid DSI hw configuration\n", __func__);
		goto error;
	}

	mdss_dsi_config_clk_src(pdev);

error:
	return rc;
}

static int mdss_dsi_remove(struct platform_device *pdev)
@@ -2770,10 +2965,6 @@ int dsi_panel_device_register(struct platform_device *ctrl_pdev,
		return -EPERM;
	}

	rc = mdss_dsi_pll_1_clk_init(ctrl_pdev, ctrl_pdata);
	if (rc)
		pr_err("PLL 1 Clock's did not register\n");

	if (pinfo->dynamic_fps &&
			pinfo->dfps_update == DFPS_IMMEDIATE_CLK_UPDATE_MODE) {
		if (mdss_dsi_shadow_clk_init(ctrl_pdev, ctrl_pdata)) {
+61 −10
Original line number Diff line number Diff line
@@ -219,6 +219,7 @@ enum {
 */
struct dsi_shared_data {
	u32 hw_config; /* DSI setup configuration i.e. single/dual/split */
	u32 pll_src_config; /* PLL source selection for DSI link clocks */
	u32 hw_rev; /* DSI h/w revision */

	/* DSI ULPS clamp register offsets */
@@ -234,6 +235,18 @@ struct dsi_shared_data {
	struct clk *axi_clk;
	struct clk *mmss_misc_ahb_clk;

	/* Other shared clocks */
	struct clk *ext_byte0_clk;
	struct clk *ext_pixel0_clk;
	struct clk *ext_byte1_clk;
	struct clk *ext_pixel1_clk;

	/* Clock sources for branch clocks */
	struct clk *byte0_parent;
	struct clk *pixel0_parent;
	struct clk *byte1_parent;
	struct clk *pixel1_parent;

	/* DSI core regulators */
	struct dss_module_power power_data[DSI_MAX_PM];
};
@@ -264,6 +277,17 @@ enum mdss_dsi_hw_config {
	SPLIT_DSI,
};

/*
 * enum mdss_dsi_pll_src_config - The PLL source for DSI link clocks
 *
 * @PLL_SRC_0:		The link clocks are sourced out of PLL0.
 * @PLL_SRC_1:		The link clocks are sourced out of PLL1.
 */
enum mdss_dsi_pll_src_config {
	PLL_SRC_0,
	PLL_SRC_1,
};

struct dsi_panel_cmds {
	char *buf;
	int blen;
@@ -300,9 +324,9 @@ struct panel_horizontal_idle {
#define DSI_CTRL_CLK_SLAVE	DSI_CTRL_RIGHT
#define DSI_CTRL_CLK_MASTER	DSI_CTRL_LEFT

#define DSI_BUS_CLKS	BIT(0)
#define DSI_CORE_CLKS	BIT(0)
#define DSI_LINK_CLKS	BIT(1)
#define DSI_ALL_CLKS	((DSI_BUS_CLKS) | (DSI_LINK_CLKS))
#define DSI_ALL_CLKS	((DSI_CORE_CLKS) | (DSI_LINK_CLKS))

#define DSI_EV_PLL_UNLOCKED		0x0001
#define DSI_EV_MDP_FIFO_UNDERFLOW	0x0002
@@ -334,7 +358,7 @@ struct mdss_dsi_ctrl_pdata {
	struct dss_io_data mmss_misc_io;
	struct dss_io_data phy_io;
	int reg_size;
	u32 bus_clk_cnt;
	u32 core_clk_cnt;
	u32 link_clk_cnt;
	u32 flags;
	struct clk *byte_clk;
@@ -346,7 +370,9 @@ struct mdss_dsi_ctrl_pdata {
	struct clk *pll_pixel_clk;
	struct clk *shadow_byte_clk;
	struct clk *shadow_pixel_clk;
	struct clk *vco_clk;
	struct clk *byte_clk_rcg;
	struct clk *pixel_clk_rcg;
	struct clk *vco_dummy_clk;
	u8 ctrl_state;
	int panel_mode;
	int irq_cnt;
@@ -483,18 +509,14 @@ int mdss_dsi_link_clk_init(struct platform_device *pdev,
		      struct mdss_dsi_ctrl_pdata *ctrl_pdata);
void mdss_dsi_link_clk_deinit(struct device *dev,
			struct mdss_dsi_ctrl_pdata *ctrl_pdata);
int mdss_dsi_bus_clk_init(struct platform_device *pdev,
int mdss_dsi_core_clk_init(struct platform_device *pdev,
			struct dsi_shared_data *sdata);
void mdss_dsi_bus_clk_deinit(struct device *dev,
void mdss_dsi_core_clk_deinit(struct device *dev,
			struct dsi_shared_data *sdata);
int mdss_dsi_shadow_clk_init(struct platform_device *pdev,
		      struct mdss_dsi_ctrl_pdata *ctrl_pdata);
void mdss_dsi_shadow_clk_deinit(struct device *dev,
			struct mdss_dsi_ctrl_pdata *ctrl_pdata);
int mdss_dsi_pll_1_clk_init(struct platform_device *pdev,
		      struct mdss_dsi_ctrl_pdata *ctrl_pdata);
int mdss_dsi_enable_bus_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata);
void mdss_dsi_disable_bus_clocks(struct mdss_dsi_ctrl_pdata *ctrl_pdata);
int mdss_dsi_panel_reset(struct mdss_panel_data *pdata, int enable);
void mdss_dsi_phy_disable(struct mdss_dsi_ctrl_pdata *ctrl);
void mdss_dsi_cmd_test_pattern(struct mdss_dsi_ctrl_pdata *ctrl);
@@ -572,6 +594,35 @@ static inline bool mdss_dsi_is_hw_config_dual(struct dsi_shared_data *sdata)
	return mdss_dsi_get_hw_config(sdata) == DUAL_DSI;
}

static inline u32 mdss_dsi_get_pll_src_config(struct dsi_shared_data *sdata)
{
	return sdata->pll_src_config;
}

/*
 * mdss_dsi_is_pll_src_pll0: Check if the PLL source for a DSI device is PLL0
 * The function is only valid if the DSI configuration is single/split DSI.
 * Not valid for dual DSI configuration.
 *
 * @sdata: pointer to DSI shared data structure
 */
static inline u32 mdss_dsi_is_pll_src_pll0(struct dsi_shared_data *sdata)
{
	return sdata->pll_src_config == PLL_SRC_0;
}

/*
 * mdss_dsi_is_pll_src_pll1: Check if the PLL source for a DSI device is PLL1
 * The function is only valid if the DSI configuration is single/split DSI.
 * Not valid for dual DSI configuration.
 *
 * @sdata: pointer to DSI shared data structure
 */
static inline u32 mdss_dsi_is_pll_src_pll1(struct dsi_shared_data *sdata)
{
	return sdata->pll_src_config == PLL_SRC_1;
}

static inline bool mdss_dsi_sync_wait_enable(struct mdss_dsi_ctrl_pdata *ctrl)
{
	return ctrl->cmd_sync_wait_broadcast;
Loading