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

Commit def4218c authored by Siddhartha Agrawal's avatar Siddhartha Agrawal Committed by Matt Wagantall
Browse files

clk: mdss: shutdown 20nm PHY pll properly to fix power issue



The second DSI PLL is consuming power when it is in reset
state. Configure the needed registers to shutdown the
second DSI PLL properly even though its not been used.
Add these register configurations whenever mdss gdsc
is toggled.

Change-Id: I008bc102795ccb5991bf4b61545c2d672b453392
Signed-off-by: default avatarChandan Uddaraju <chandanu@codeaurora.org>
Signed-off-by: default avatarSiddhartha Agrawal <agrawals@codeaurora.org>
parent 82dd0b7b
Loading
Loading
Loading
Loading
+23 −2
Original line number Diff line number Diff line
@@ -481,6 +481,19 @@ int dsi_20nm_pll_lock_status(struct mdss_pll_resources *dsi_pll_res)
	return pll_locked;
}

void __dsi_pll_disable(void __iomem *pll_base)
{
	if (!pll_base) {
		pr_err("Invalid pll base.\n");
		return;
	}
	pr_debug("Disabling PHY PLL for PLL_BASE=%p\n", pll_base);

	MDSS_PLL_REG_W(pll_base, MMSS_DSI_PHY_PLL_PLL_VCOTAIL_EN, 0x042);
	MDSS_PLL_REG_W(pll_base, MMSS_DSI_PHY_PLL_BIAS_EN_CLKBUFLR_EN, 0x02);
	MDSS_PLL_REG_W(pll_base, MMSS_DSI_PHY_PLL_RESETSM_CNTRL3, 0x02);
}

static int dsi_pll_enable(struct clk *c)
{
	int i, rc;
@@ -501,6 +514,9 @@ static int dsi_pll_enable(struct clk *c)
		if (!rc)
			break;
	}
	/* Disable PLL1 to avoid current leakage while toggling MDSS GDSC */
	if (dsi_pll_res->pll_1_base)
		__dsi_pll_disable(dsi_pll_res->pll_1_base);

	if (rc) {
		mdss_pll_resource_enable(dsi_pll_res, false);
@@ -524,8 +540,11 @@ static void dsi_pll_disable(struct clk *c)

	dsi_pll_res->handoff_resources = false;

	MDSS_PLL_REG_W(dsi_pll_res->pll_base,
				MMSS_DSI_PHY_PLL_PLL_VCOTAIL_EN, 0x02);
	__dsi_pll_disable(dsi_pll_res->pll_base);

	/* Disable PLL1 to avoid current leakage while toggling MDSS GDSC */
	if (dsi_pll_res->pll_1_base)
		__dsi_pll_disable(dsi_pll_res->pll_1_base);

	mdss_pll_resource_enable(dsi_pll_res, false);
	dsi_pll_res->pll_on = false;
@@ -778,6 +797,8 @@ int pll_20nm_vco_set_rate(struct dsi_pll_vco_clk *vco, unsigned long rate)
	 */
	udelay(1000);
	wmb();
	if (dsi_pll_res->pll_1_base)
		__dsi_pll_disable(dsi_pll_res->pll_1_base);
	return 0;
}

+62 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/clk/msm-clk-provider.h>
#include <linux/clk/msm-clk.h>
#include <linux/workqueue.h>
#include <linux/clk/msm-clock-generic.h>
#include <dt-bindings/clock/msm-clocks-8994.h>

@@ -47,6 +48,8 @@ static int vco_set_rate_20nm(struct clk *c, unsigned long rate)
		return rc;
	}

	pr_debug("Cancel pending pll off work\n");
	cancel_work_sync(&dsi_pll_res->pll_off);
	rc = pll_20nm_vco_set_rate(vco, rate);

	mdss_pll_resource_enable(dsi_pll_res, false);
@@ -453,10 +456,58 @@ static struct clk_lookup mdss_dsi_pllcc_8994[] = {
	CLK_LIST(shadow_dsi_vco_clk_8994),
};

static void dsi_pll_off_work(struct work_struct *work)
{
	struct mdss_pll_resources *pll_res;

	if (!work) {
		pr_err("pll_resource is invalid\n");
		return;
	}

	pr_debug("Starting PLL off Worker%s\n", __func__);

	pll_res = container_of(work, struct
			mdss_pll_resources, pll_off);

	mdss_pll_resource_enable(pll_res, true);
	__dsi_pll_disable(pll_res->pll_base);
	if (pll_res->pll_1_base)
		__dsi_pll_disable(pll_res->pll_1_base);
	mdss_pll_resource_enable(pll_res, false);
}

static int dsi_pll_regulator_notifier_call(struct notifier_block *self,
		unsigned long event, void *data)
{

	struct mdss_pll_resources *pll_res;

	if (!self) {
		pr_err("pll_resource is invalid\n");
		goto error;
	}

	pll_res = container_of(self, struct
			mdss_pll_resources, gdsc_cb);

	if (event & REGULATOR_EVENT_ENABLE) {
		pr_debug("Regulator ON event. Scheduling pll off worker\n");
		schedule_work(&pll_res->pll_off);
	}

	if (event & REGULATOR_EVENT_DISABLE)
		pr_debug("Regulator OFF event.\n");

error:
	return NOTIFY_OK;
}

int dsi_pll_clock_register_20nm(struct platform_device *pdev,
				struct mdss_pll_resources *pll_res)
{
	int rc;
	struct dss_vreg *pll_reg;

	if (!pdev || !pdev->dev.of_node) {
		pr_err("Invalid input parameters\n");
@@ -513,12 +564,23 @@ int dsi_pll_clock_register_20nm(struct platform_device *pdev,
	shadow_byte_clk_src_ops.prepare = dsi_pll_div_prepare;

	if (pll_res->target_id == MDSS_PLL_TARGET_8994) {
		pll_res->gdsc_cb.notifier_call =
			dsi_pll_regulator_notifier_call;
		INIT_WORK(&pll_res->pll_off, dsi_pll_off_work);

		rc = of_msm_clock_register(pdev->dev.of_node,
			mdss_dsi_pllcc_8994, ARRAY_SIZE(mdss_dsi_pllcc_8994));
		if (rc) {
			pr_err("Clock register failed\n");
			rc = -EPROBE_DEFER;
		}
		pll_reg = mdss_pll_get_mp_by_reg_name(pll_res, "gdsc");
		if (pll_reg) {
			pr_debug("Registering for gdsc regulator events\n");
			if (regulator_register_notifier(pll_reg->vreg,
						&(pll_res->gdsc_cb)))
				pr_err("Regulator notification registration failed!\n");
		}
	} else {
		pr_err("Invalid target ID\n");
		rc = -EINVAL;
+1 −0
Original line number Diff line number Diff line
@@ -99,6 +99,7 @@ int hr_oclk3_get_div(struct div_clk *clk);
int ndiv_set_div(struct div_clk *clk, int div);
int shadow_ndiv_set_div(struct div_clk *clk, int div);
int ndiv_get_div(struct div_clk *clk);
void __dsi_pll_disable(void __iomem *pll_base);

int set_mdss_pixel_mux_sel(struct mux_clk *clk, int sel);
int get_mdss_pixel_mux_sel(struct mux_clk *clk);
+34 −0
Original line number Diff line number Diff line
@@ -47,6 +47,40 @@ vreg_err:
	return rc;
}

/**
 * mdss_pll_get_mp_by_reg_name() -- Find power module by regulator name
 *@pll_res: Pointer to the PLL resource
 *@name: Regulator name as specified in the pll dtsi
 *
 * This is a helper function to retrieve the regulator information
 * for each pll resource.
 */
struct dss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res
		, char *name)
{

	struct dss_vreg *regulator = NULL;
	int i;

	if ((pll_res == NULL) || (pll_res->mp.vreg_config == NULL)) {
		pr_err("%s Invalid PLL resource\n", __func__);
		goto error;
	}

	regulator = pll_res->mp.vreg_config;

	for (i = 0; i < pll_res->mp.num_vreg; i++) {
		if (!strcmp(name, regulator->vreg_name)) {
			pr_debug("Found regulator match for %s\n", name);
			break;
		}
		regulator++;
	}

error:
	return regulator;
}

void mdss_pll_util_resource_deinit(struct platform_device *pdev,
					 struct mdss_pll_resources *pll_res)
{
+21 −0
Original line number Diff line number Diff line
@@ -249,6 +249,25 @@ static int mdss_pll_probe(struct platform_device *pdev)
		goto res_parse_error;
	}

	/*
	 * DSI PLL 1 is leaking current whenever MDSS GDSC is toggled. Need to
	 * map PLL1 registers along with the PLl0 so that we can manually turn
	 * off PLL1.
	 */
	if (pll_res->pll_interface_type == MDSS_DSI_PLL_20NM) {
		struct resource *pll_1_base_reg;
		pll_1_base_reg = platform_get_resource_byname(pdev,
				IORESOURCE_MEM, "pll_1_base");
		if (pll_1_base_reg) {
			pll_res->pll_1_base = ioremap(pll_1_base_reg->start,
					resource_size(pll_1_base_reg));
			if (!pll_res->pll_1_base)
				pr_err("Unable to remap pll 1 base resources\n");
		} else {
			pr_err("Unable to get the pll 1 base resource\n");
		}
	}

	phy_base_reg = platform_get_resource_byname(pdev,
						IORESOURCE_MEM, "phy_base");
	if (!phy_base_reg) {
@@ -303,6 +322,8 @@ dyn_pll_io_error:
	if (pll_res->phy_base)
		iounmap(pll_res->phy_base);
phy_io_error:
	if (pll_res->pll_1_base)
		iounmap(pll_res->pll_1_base);
	mdss_pll_resource_release(pdev, pll_res);
res_parse_error:
	iounmap(pll_res->pll_base);
Loading