From 73dbf69638b760ce62fe6df4cece983ef28abc13 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 4 May 2015 19:03:54 +0200 Subject: drm/msm: use IS_ERR() to check msm_ioremap() return msm_ioremap() never returns NULL. There's no need for IS_ERR_OR_NULL() Signed-off-by: Fabian Frederick Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/dsi/dsi_phy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/msm/dsi/dsi_phy.c') diff --git a/drivers/gpu/drm/msm/dsi/dsi_phy.c b/drivers/gpu/drm/msm/dsi/dsi_phy.c index f0cea8927388..61af19312711 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/dsi_phy.c @@ -300,12 +300,12 @@ struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, return NULL; phy->base = msm_ioremap(pdev, "dsi_phy", "DSI_PHY"); - if (IS_ERR_OR_NULL(phy->base)) { + if (IS_ERR(phy->base)) { pr_err("%s: failed to map phy base\n", __func__); return NULL; } phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", "DSI_PHY_REG"); - if (IS_ERR_OR_NULL(phy->reg_base)) { + if (IS_ERR(phy->reg_base)) { pr_err("%s: failed to map phy regulator base\n", __func__); return NULL; } -- cgit v1.2.3-59-g8ed1b From 825637b9c06cede2a742421b0ea6f24428099af3 Mon Sep 17 00:00:00 2001 From: Hai Li Date: Fri, 15 May 2015 13:04:04 -0400 Subject: drm/msm/dsi: Add DSI PLL clock driver support DSI byte clock and pixel clocks are sourced from DSI PLL. This change adds the DSI PLL source clock driver under common clock framework. This change handles DSI 28nm PLL only. Signed-off-by: Hai Li Signed-off-by: Archit Taneja Signed-off-by: Stephane Viau Signed-off-by: Wentao Xu Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/Kconfig | 7 + drivers/gpu/drm/msm/Makefile | 5 + drivers/gpu/drm/msm/dsi/dsi.h | 3 +- drivers/gpu/drm/msm/dsi/dsi_host.c | 10 +- drivers/gpu/drm/msm/dsi/dsi_phy.c | 3 +- drivers/gpu/drm/msm/dsi/pll/dsi_pll.c | 164 ++++++++ drivers/gpu/drm/msm/dsi/pll/dsi_pll.h | 89 ++++ drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c | 652 +++++++++++++++++++++++++++++ 8 files changed, 926 insertions(+), 7 deletions(-) create mode 100644 drivers/gpu/drm/msm/dsi/pll/dsi_pll.c create mode 100644 drivers/gpu/drm/msm/dsi/pll/dsi_pll.h create mode 100644 drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c (limited to 'drivers/gpu/drm/msm/dsi/dsi_phy.c') diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index 0a6f6764a37c..08ba8d0d93f5 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -46,3 +46,10 @@ config DRM_MSM_DSI Choose this option if you have a need for MIPI DSI connector support. +config DRM_MSM_DSI_PLL + bool "Enable DSI PLL driver in MSM DRM" + depends on DRM_MSM_DSI && COMMON_CLK + default y + help + Choose this option to enable DSI PLL driver which provides DSI + source clocks under common clock framework. diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index ab2086783fee..16a81b94d6f0 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -1,4 +1,5 @@ ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/msm +ccflags-$(CONFIG_DRM_MSM_DSI_PLL) += -Idrivers/gpu/drm/msm/dsi msm-y := \ adreno/adreno_device.o \ @@ -50,10 +51,14 @@ msm-y := \ msm-$(CONFIG_DRM_MSM_FBDEV) += msm_fbdev.o msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o + msm-$(CONFIG_DRM_MSM_DSI) += dsi/dsi.o \ dsi/dsi_host.o \ dsi/dsi_manager.o \ dsi/dsi_phy.o \ mdp/mdp5/mdp5_cmd_encoder.o +msm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \ + dsi/pll/dsi_pll_28nm.o + obj-$(CONFIG_DRM_MSM) += msm.o diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 10f54d4e379a..321964a6b27e 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -103,7 +103,8 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi); struct msm_dsi_phy; enum msm_dsi_phy_type { MSM_DSI_PHY_UNKNOWN, - MSM_DSI_PHY_28NM, + MSM_DSI_PHY_28NM_HPM, + MSM_DSI_PHY_28NM_LP, MSM_DSI_PHY_MAX }; struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 517e0709b77d..8a246cfa5d19 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -64,7 +64,7 @@ static const struct dsi_config dsi_cfgs[] = { .major = MSM_DSI_VER_MAJOR_6G, .minor = MSM_DSI_6G_VER_MINOR_V1_0, .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM, + .phy_type = MSM_DSI_PHY_28NM_HPM, .reg_cfg = { .num = 4, .regs = { @@ -79,7 +79,7 @@ static const struct dsi_config dsi_cfgs[] = { .major = MSM_DSI_VER_MAJOR_6G, .minor = MSM_DSI_6G_VER_MINOR_V1_1, .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM, + .phy_type = MSM_DSI_PHY_28NM_HPM, .reg_cfg = { .num = 4, .regs = { @@ -94,7 +94,7 @@ static const struct dsi_config dsi_cfgs[] = { .major = MSM_DSI_VER_MAJOR_6G, .minor = MSM_DSI_6G_VER_MINOR_V1_1_1, .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM, + .phy_type = MSM_DSI_PHY_28NM_HPM, .reg_cfg = { .num = 4, .regs = { @@ -109,7 +109,7 @@ static const struct dsi_config dsi_cfgs[] = { .major = MSM_DSI_VER_MAJOR_6G, .minor = MSM_DSI_6G_VER_MINOR_V1_2, .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM, + .phy_type = MSM_DSI_PHY_28NM_HPM, .reg_cfg = { .num = 4, .regs = { @@ -124,7 +124,7 @@ static const struct dsi_config dsi_cfgs[] = { .major = MSM_DSI_VER_MAJOR_6G, .minor = MSM_DSI_6G_VER_MINOR_V1_3_1, .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM, + .phy_type = MSM_DSI_PHY_28NM_LP, .reg_cfg = { .num = 4, .regs = { diff --git a/drivers/gpu/drm/msm/dsi/dsi_phy.c b/drivers/gpu/drm/msm/dsi/dsi_phy.c index 61af19312711..4403f38bf220 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/dsi_phy.c @@ -311,7 +311,8 @@ struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, } switch (type) { - case MSM_DSI_PHY_28NM: + case MSM_DSI_PHY_28NM_HPM: + case MSM_DSI_PHY_28NM_LP: dsi_phy_func_init(28nm); break; default: diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c new file mode 100644 index 000000000000..509376fdd112 --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2012-2015, 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. + */ + +#include "dsi_pll.h" + +static int dsi_pll_enable(struct msm_dsi_pll *pll) +{ + int i, ret = 0; + + /* + * Certain PLLs do not allow VCO rate update when it is on. + * Keep track of their status to turn on/off after set rate success. + */ + if (unlikely(pll->pll_on)) + return 0; + + /* Try all enable sequences until one succeeds */ + for (i = 0; i < pll->en_seq_cnt; i++) { + ret = pll->enable_seqs[i](pll); + DBG("DSI PLL %s after sequence #%d", + ret ? "unlocked" : "locked", i + 1); + if (!ret) + break; + } + + if (ret) { + DRM_ERROR("DSI PLL failed to lock\n"); + return ret; + } + + pll->pll_on = true; + + return 0; +} + +static void dsi_pll_disable(struct msm_dsi_pll *pll) +{ + if (unlikely(!pll->pll_on)) + return; + + pll->disable_seq(pll); + + pll->pll_on = false; +} + +/* + * DSI PLL Helper functions + */ +long msm_dsi_pll_helper_clk_round_rate(struct clk_hw *hw, + unsigned long rate, unsigned long *parent_rate) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + + if (rate < pll->min_rate) + return pll->min_rate; + else if (rate > pll->max_rate) + return pll->max_rate; + else + return rate; +} + +int msm_dsi_pll_helper_clk_prepare(struct clk_hw *hw) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + int ret; + + /* + * Certain PLLs need to update the same VCO rate and registers + * after resume in suspend/resume scenario. + */ + if (pll->restore_state) { + ret = pll->restore_state(pll); + if (ret) + goto error; + } + + ret = dsi_pll_enable(pll); + +error: + return ret; +} + +void msm_dsi_pll_helper_clk_unprepare(struct clk_hw *hw) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + + if (pll->save_state) + pll->save_state(pll); + + dsi_pll_disable(pll); +} + +void msm_dsi_pll_helper_unregister_clks(struct platform_device *pdev, + struct clk **clks, u32 num_clks) +{ + of_clk_del_provider(pdev->dev.of_node); + + if (!num_clks || !clks) + return; + + do { + clk_unregister(clks[--num_clks]); + clks[num_clks] = NULL; + } while (num_clks); +} + +/* + * DSI PLL API + */ +int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll, + struct clk **byte_clk_provider, struct clk **pixel_clk_provider) +{ + if (pll->get_provider) + return pll->get_provider(pll, + byte_clk_provider, + pixel_clk_provider); + + return -EINVAL; +} + +void msm_dsi_pll_destroy(struct msm_dsi_pll *pll) +{ + if (pll->destroy) + pll->destroy(pll); +} + +struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, + enum msm_dsi_phy_type type, int id) +{ + struct device *dev = &pdev->dev; + struct msm_dsi_pll *pll; + + switch (type) { + case MSM_DSI_PHY_28NM_HPM: + case MSM_DSI_PHY_28NM_LP: + pll = msm_dsi_pll_28nm_init(pdev, type, id); + break; + default: + pll = ERR_PTR(-ENXIO); + break; + } + + if (IS_ERR(pll)) { + dev_err(dev, "%s: failed to init DSI PLL\n", __func__); + return NULL; + } + + pll->type = type; + + DBG("DSI:%d PLL registered", id); + + return pll; +} + diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h new file mode 100644 index 000000000000..5a3bb241c039 --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2012-2015, 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. + */ + +#ifndef __DSI_PLL_H__ +#define __DSI_PLL_H__ + +#include +#include + +#include "dsi.h" + +#define NUM_DSI_CLOCKS_MAX 6 +#define MAX_DSI_PLL_EN_SEQS 10 + +struct msm_dsi_pll { + enum msm_dsi_phy_type type; + + struct clk_hw clk_hw; + bool pll_on; + + unsigned long min_rate; + unsigned long max_rate; + u32 en_seq_cnt; + + int (*enable_seqs[MAX_DSI_PLL_EN_SEQS])(struct msm_dsi_pll *pll); + void (*disable_seq)(struct msm_dsi_pll *pll); + int (*get_provider)(struct msm_dsi_pll *pll, + struct clk **byte_clk_provider, + struct clk **pixel_clk_provider); + void (*destroy)(struct msm_dsi_pll *pll); + void (*save_state)(struct msm_dsi_pll *pll); + int (*restore_state)(struct msm_dsi_pll *pll); +}; + +#define hw_clk_to_pll(x) container_of(x, struct msm_dsi_pll, clk_hw) + +static inline void pll_write(void __iomem *reg, u32 data) +{ + msm_writel(data, reg); +} + +static inline u32 pll_read(const void __iomem *reg) +{ + return msm_readl(reg); +} + +static inline void pll_write_udelay(void __iomem *reg, u32 data, u32 delay_us) +{ + pll_write(reg, data); + udelay(delay_us); +} + +static inline void pll_write_ndelay(void __iomem *reg, u32 data, u32 delay_ns) +{ + pll_write((reg), data); + ndelay(delay_ns); +} + +/* + * DSI PLL Helper functions + */ + +/* clock callbacks */ +long msm_dsi_pll_helper_clk_round_rate(struct clk_hw *hw, + unsigned long rate, unsigned long *parent_rate); +int msm_dsi_pll_helper_clk_prepare(struct clk_hw *hw); +void msm_dsi_pll_helper_clk_unprepare(struct clk_hw *hw); +/* misc */ +void msm_dsi_pll_helper_unregister_clks(struct platform_device *pdev, + struct clk **clks, u32 num_clks); + +/* + * Initialization for Each PLL Type + */ +struct msm_dsi_pll *msm_dsi_pll_28nm_init(struct platform_device *pdev, + enum msm_dsi_phy_type type, int id); + +#endif /* __DSI_PLL_H__ */ + diff --git a/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c new file mode 100644 index 000000000000..eb8ac3097ff5 --- /dev/null +++ b/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c @@ -0,0 +1,652 @@ +/* + * Copyright (c) 2012-2015, 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. + */ + +#include +#include + +#include "dsi_pll.h" +#include "dsi.xml.h" + +/* + * DSI PLL 28nm - clock diagram (eg: DSI0): + * + * dsi0analog_postdiv_clk + * | dsi0indirect_path_div2_clk + * | | + * +------+ | +----+ | |\ dsi0byte_mux + * dsi0vco_clk --o--| DIV1 |--o--| /2 |--o--| \ | + * | +------+ +----+ | m| | +----+ + * | | u|--o--| /4 |-- dsi0pllbyte + * | | x| +----+ + * o--------------------------| / + * | |/ + * | +------+ + * o----------| DIV3 |------------------------- dsi0pll + * +------+ + */ + +#define POLL_MAX_READS 10 +#define POLL_TIMEOUT_US 50 + +#define NUM_PROVIDED_CLKS 2 + +#define VCO_REF_CLK_RATE 19200000 +#define VCO_MIN_RATE 350000000 +#define VCO_MAX_RATE 750000000 + +#define DSI_BYTE_PLL_CLK 0 +#define DSI_PIXEL_PLL_CLK 1 + +#define LPFR_LUT_SIZE 10 +struct lpfr_cfg { + unsigned long vco_rate; + u32 resistance; +}; + +/* Loop filter resistance: */ +static const struct lpfr_cfg lpfr_lut[LPFR_LUT_SIZE] = { + { 479500000, 8 }, + { 480000000, 11 }, + { 575500000, 8 }, + { 576000000, 12 }, + { 610500000, 8 }, + { 659500000, 9 }, + { 671500000, 10 }, + { 672000000, 14 }, + { 708500000, 10 }, + { 750000000, 11 }, +}; + +struct pll_28nm_cached_state { + unsigned long vco_rate; + u8 postdiv3; + u8 postdiv1; + u8 byte_mux; +}; + +struct dsi_pll_28nm { + struct msm_dsi_pll base; + + int id; + struct platform_device *pdev; + void __iomem *mmio; + + int vco_delay; + + /* private clocks: */ + struct clk *clks[NUM_DSI_CLOCKS_MAX]; + u32 num_clks; + + /* clock-provider: */ + struct clk *provided_clks[NUM_PROVIDED_CLKS]; + struct clk_onecell_data clk_data; + + struct pll_28nm_cached_state cached_state; +}; + +#define to_pll_28nm(x) container_of(x, struct dsi_pll_28nm, base) + +static bool pll_28nm_poll_for_ready(struct dsi_pll_28nm *pll_28nm, + u32 nb_tries, u32 timeout_us) +{ + bool pll_locked = false; + u32 val; + + while (nb_tries--) { + val = pll_read(pll_28nm->mmio + REG_DSI_28nm_PHY_PLL_STATUS); + pll_locked = !!(val & DSI_28nm_PHY_PLL_STATUS_PLL_RDY); + + if (pll_locked) + break; + + udelay(timeout_us); + } + DBG("DSI PLL is %slocked", pll_locked ? "" : "*not* "); + + return pll_locked; +} + +static void pll_28nm_software_reset(struct dsi_pll_28nm *pll_28nm) +{ + void __iomem *base = pll_28nm->mmio; + + /* + * Add HW recommended delays after toggling the software + * reset bit off and back on. + */ + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_TEST_CFG, + DSI_28nm_PHY_PLL_TEST_CFG_PLL_SW_RESET, 1); + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_TEST_CFG, 0x00, 1); +} + +/* + * Clock Callbacks + */ +static int dsi_pll_28nm_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + struct device *dev = &pll_28nm->pdev->dev; + void __iomem *base = pll_28nm->mmio; + unsigned long div_fbx1000, gen_vco_clk; + u32 refclk_cfg, frac_n_mode, frac_n_value; + u32 sdm_cfg0, sdm_cfg1, sdm_cfg2, sdm_cfg3; + u32 cal_cfg10, cal_cfg11; + u32 rem; + int i; + + VERB("rate=%lu, parent's=%lu", rate, parent_rate); + + /* Force postdiv2 to be div-4 */ + pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV2_CFG, 3); + + /* Configure the Loop filter resistance */ + for (i = 0; i < LPFR_LUT_SIZE; i++) + if (rate <= lpfr_lut[i].vco_rate) + break; + if (i == LPFR_LUT_SIZE) { + dev_err(dev, "unable to get loop filter resistance. vco=%lu\n", + rate); + return -EINVAL; + } + pll_write(base + REG_DSI_28nm_PHY_PLL_LPFR_CFG, lpfr_lut[i].resistance); + + /* Loop filter capacitance values : c1 and c2 */ + pll_write(base + REG_DSI_28nm_PHY_PLL_LPFC1_CFG, 0x70); + pll_write(base + REG_DSI_28nm_PHY_PLL_LPFC2_CFG, 0x15); + + rem = rate % VCO_REF_CLK_RATE; + if (rem) { + refclk_cfg = DSI_28nm_PHY_PLL_REFCLK_CFG_DBLR; + frac_n_mode = 1; + div_fbx1000 = rate / (VCO_REF_CLK_RATE / 500); + gen_vco_clk = div_fbx1000 * (VCO_REF_CLK_RATE / 500); + } else { + refclk_cfg = 0x0; + frac_n_mode = 0; + div_fbx1000 = rate / (VCO_REF_CLK_RATE / 1000); + gen_vco_clk = div_fbx1000 * (VCO_REF_CLK_RATE / 1000); + } + + DBG("refclk_cfg = %d", refclk_cfg); + + rem = div_fbx1000 % 1000; + frac_n_value = (rem << 16) / 1000; + + DBG("div_fb = %lu", div_fbx1000); + DBG("frac_n_value = %d", frac_n_value); + + DBG("Generated VCO Clock: %lu", gen_vco_clk); + rem = 0; + sdm_cfg1 = pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1); + sdm_cfg1 &= ~DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__MASK; + if (frac_n_mode) { + sdm_cfg0 = 0x0; + sdm_cfg0 |= DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV(0); + sdm_cfg1 |= DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET( + (u32)(((div_fbx1000 / 1000) & 0x3f) - 1)); + sdm_cfg3 = frac_n_value >> 8; + sdm_cfg2 = frac_n_value & 0xff; + } else { + sdm_cfg0 = DSI_28nm_PHY_PLL_SDM_CFG0_BYP; + sdm_cfg0 |= DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV( + (u32)(((div_fbx1000 / 1000) & 0x3f) - 1)); + sdm_cfg1 |= DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET(0); + sdm_cfg2 = 0; + sdm_cfg3 = 0; + } + + DBG("sdm_cfg0=%d", sdm_cfg0); + DBG("sdm_cfg1=%d", sdm_cfg1); + DBG("sdm_cfg2=%d", sdm_cfg2); + DBG("sdm_cfg3=%d", sdm_cfg3); + + cal_cfg11 = (u32)(gen_vco_clk / (256 * 1000000)); + cal_cfg10 = (u32)((gen_vco_clk % (256 * 1000000)) / 1000000); + DBG("cal_cfg10=%d, cal_cfg11=%d", cal_cfg10, cal_cfg11); + + pll_write(base + REG_DSI_28nm_PHY_PLL_CHGPUMP_CFG, 0x02); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG3, 0x2b); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG4, 0x06); + pll_write(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x0d); + + pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1, sdm_cfg1); + pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG2, + DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0(sdm_cfg2)); + pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG3, + DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8(sdm_cfg3)); + pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG4, 0x00); + + /* Add hardware recommended delay for correct PLL configuration */ + if (pll_28nm->vco_delay) + udelay(pll_28nm->vco_delay); + + pll_write(base + REG_DSI_28nm_PHY_PLL_REFCLK_CFG, refclk_cfg); + pll_write(base + REG_DSI_28nm_PHY_PLL_PWRGEN_CFG, 0x00); + pll_write(base + REG_DSI_28nm_PHY_PLL_VCOLPF_CFG, 0x31); + pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0, sdm_cfg0); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG0, 0x12); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG6, 0x30); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG7, 0x00); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG8, 0x60); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG9, 0x00); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG10, cal_cfg10 & 0xff); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG11, cal_cfg11 & 0xff); + pll_write(base + REG_DSI_28nm_PHY_PLL_EFUSE_CFG, 0x20); + + return 0; +} + +static int dsi_pll_28nm_clk_is_enabled(struct clk_hw *hw) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + + return pll_28nm_poll_for_ready(pll_28nm, POLL_MAX_READS, + POLL_TIMEOUT_US); +} + +static unsigned long dsi_pll_28nm_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + void __iomem *base = pll_28nm->mmio; + u32 sdm0, doubler, sdm_byp_div; + u32 sdm_dc_off, sdm_freq_seed, sdm2, sdm3; + u32 ref_clk = VCO_REF_CLK_RATE; + unsigned long vco_rate; + + VERB("parent_rate=%lu", parent_rate); + + /* Check to see if the ref clk doubler is enabled */ + doubler = pll_read(base + REG_DSI_28nm_PHY_PLL_REFCLK_CFG) & + DSI_28nm_PHY_PLL_REFCLK_CFG_DBLR; + ref_clk += (doubler * VCO_REF_CLK_RATE); + + /* see if it is integer mode or sdm mode */ + sdm0 = pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0); + if (sdm0 & DSI_28nm_PHY_PLL_SDM_CFG0_BYP) { + /* integer mode */ + sdm_byp_div = FIELD( + pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0), + DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV) + 1; + vco_rate = ref_clk * sdm_byp_div; + } else { + /* sdm mode */ + sdm_dc_off = FIELD( + pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1), + DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET); + DBG("sdm_dc_off = %d", sdm_dc_off); + sdm2 = FIELD(pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG2), + DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0); + sdm3 = FIELD(pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG3), + DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8); + sdm_freq_seed = (sdm3 << 8) | sdm2; + DBG("sdm_freq_seed = %d", sdm_freq_seed); + + vco_rate = (ref_clk * (sdm_dc_off + 1)) + + mult_frac(ref_clk, sdm_freq_seed, BIT(16)); + DBG("vco rate = %lu", vco_rate); + } + + DBG("returning vco rate = %lu", vco_rate); + + return vco_rate; +} + +static const struct clk_ops clk_ops_dsi_pll_28nm_vco = { + .round_rate = msm_dsi_pll_helper_clk_round_rate, + .set_rate = dsi_pll_28nm_clk_set_rate, + .recalc_rate = dsi_pll_28nm_clk_recalc_rate, + .prepare = msm_dsi_pll_helper_clk_prepare, + .unprepare = msm_dsi_pll_helper_clk_unprepare, + .is_enabled = dsi_pll_28nm_clk_is_enabled, +}; + +/* + * PLL Callbacks + */ +static int dsi_pll_28nm_enable_seq_hpm(struct msm_dsi_pll *pll) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + struct device *dev = &pll_28nm->pdev->dev; + void __iomem *base = pll_28nm->mmio; + u32 max_reads = 5, timeout_us = 100; + bool locked; + u32 val; + int i; + + DBG("id=%d", pll_28nm->id); + + pll_28nm_software_reset(pll_28nm); + + /* + * PLL power up sequence. + * Add necessary delays recommended by hardware. + */ + val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 1); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 600); + + for (i = 0; i < 2; i++) { + /* DSI Uniphy lock detect setting */ + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, + 0x0c, 100); + pll_write(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x0d); + + /* poll for PLL ready status */ + locked = pll_28nm_poll_for_ready(pll_28nm, + max_reads, timeout_us); + if (locked) + break; + + pll_28nm_software_reset(pll_28nm); + + /* + * PLL power up sequence. + * Add necessary delays recommended by hardware. + */ + val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 1); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 250); + + val &= ~DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 600); + } + + if (unlikely(!locked)) + dev_err(dev, "DSI PLL lock failed\n"); + else + DBG("DSI PLL Lock success"); + + return locked ? 0 : -EINVAL; +} + +static int dsi_pll_28nm_enable_seq_lp(struct msm_dsi_pll *pll) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + struct device *dev = &pll_28nm->pdev->dev; + void __iomem *base = pll_28nm->mmio; + bool locked; + u32 max_reads = 10, timeout_us = 50; + u32 val; + + DBG("id=%d", pll_28nm->id); + + pll_28nm_software_reset(pll_28nm); + + /* + * PLL power up sequence. + * Add necessary delays recommended by hardware. + */ + pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_CAL_CFG1, 0x34, 500); + + val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B; + pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B; + pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B | + DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE; + pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500); + + /* DSI PLL toggle lock detect setting */ + pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x04, 500); + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x05, 512); + + locked = pll_28nm_poll_for_ready(pll_28nm, max_reads, timeout_us); + + if (unlikely(!locked)) + dev_err(dev, "DSI PLL lock failed\n"); + else + DBG("DSI PLL lock success"); + + return locked ? 0 : -EINVAL; +} + +static void dsi_pll_28nm_disable_seq(struct msm_dsi_pll *pll) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + + DBG("id=%d", pll_28nm->id); + pll_write(pll_28nm->mmio + REG_DSI_28nm_PHY_PLL_GLB_CFG, 0x00); +} + +static void dsi_pll_28nm_save_state(struct msm_dsi_pll *pll) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state; + void __iomem *base = pll_28nm->mmio; + + cached_state->postdiv3 = + pll_read(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG); + cached_state->postdiv1 = + pll_read(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG); + cached_state->byte_mux = pll_read(base + REG_DSI_28nm_PHY_PLL_VREG_CFG); + cached_state->vco_rate = __clk_get_rate(pll->clk_hw.clk); +} + +static int dsi_pll_28nm_restore_state(struct msm_dsi_pll *pll) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state; + void __iomem *base = pll_28nm->mmio; + int ret; + + if ((cached_state->vco_rate != 0) && + (cached_state->vco_rate == __clk_get_rate(pll->clk_hw.clk))) { + ret = dsi_pll_28nm_clk_set_rate(&pll->clk_hw, + cached_state->vco_rate, 0); + if (ret) { + dev_err(&pll_28nm->pdev->dev, + "restore vco rate failed. ret=%d\n", ret); + return ret; + } + + pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG, + cached_state->postdiv3); + pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG, + cached_state->postdiv1); + pll_write(base + REG_DSI_28nm_PHY_PLL_VREG_CFG, + cached_state->byte_mux); + + cached_state->vco_rate = 0; + } + + return 0; +} + +static int dsi_pll_28nm_get_provider(struct msm_dsi_pll *pll, + struct clk **byte_clk_provider, + struct clk **pixel_clk_provider) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + + if (byte_clk_provider) + *byte_clk_provider = pll_28nm->provided_clks[DSI_BYTE_PLL_CLK]; + if (pixel_clk_provider) + *pixel_clk_provider = + pll_28nm->provided_clks[DSI_PIXEL_PLL_CLK]; + + return 0; +} + +static void dsi_pll_28nm_destroy(struct msm_dsi_pll *pll) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + int i; + + msm_dsi_pll_helper_unregister_clks(pll_28nm->pdev, + pll_28nm->clks, pll_28nm->num_clks); + + for (i = 0; i < NUM_PROVIDED_CLKS; i++) + pll_28nm->provided_clks[i] = NULL; + + pll_28nm->num_clks = 0; + pll_28nm->clk_data.clks = NULL; + pll_28nm->clk_data.clk_num = 0; +} + +static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm) +{ + char clk_name[32], parent1[32], parent2[32], vco_name[32]; + struct clk_init_data vco_init = { + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .name = vco_name, + .ops = &clk_ops_dsi_pll_28nm_vco, + }; + struct device *dev = &pll_28nm->pdev->dev; + struct clk **clks = pll_28nm->clks; + struct clk **provided_clks = pll_28nm->provided_clks; + int num = 0; + int ret; + + DBG("%d", pll_28nm->id); + + snprintf(vco_name, 32, "dsi%dvco_clk", pll_28nm->id); + pll_28nm->base.clk_hw.init = &vco_init; + clks[num++] = clk_register(dev, &pll_28nm->base.clk_hw); + + snprintf(clk_name, 32, "dsi%danalog_postdiv_clk", pll_28nm->id); + snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id); + clks[num++] = clk_register_divider(dev, clk_name, + parent1, CLK_SET_RATE_PARENT, + pll_28nm->mmio + + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG, + 0, 4, 0, NULL); + + snprintf(clk_name, 32, "dsi%dindirect_path_div2_clk", pll_28nm->id); + snprintf(parent1, 32, "dsi%danalog_postdiv_clk", pll_28nm->id); + clks[num++] = clk_register_fixed_factor(dev, clk_name, + parent1, CLK_SET_RATE_PARENT, + 1, 2); + + snprintf(clk_name, 32, "dsi%dpll", pll_28nm->id); + snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id); + clks[num++] = provided_clks[DSI_PIXEL_PLL_CLK] = + clk_register_divider(dev, clk_name, + parent1, 0, pll_28nm->mmio + + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG, + 0, 8, 0, NULL); + + snprintf(clk_name, 32, "dsi%dbyte_mux", pll_28nm->id); + snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id); + snprintf(parent2, 32, "dsi%dindirect_path_div2_clk", pll_28nm->id); + clks[num++] = clk_register_mux(dev, clk_name, + (const char *[]){ + parent1, parent2 + }, 2, CLK_SET_RATE_PARENT, pll_28nm->mmio + + REG_DSI_28nm_PHY_PLL_VREG_CFG, 1, 1, 0, NULL); + + snprintf(clk_name, 32, "dsi%dpllbyte", pll_28nm->id); + snprintf(parent1, 32, "dsi%dbyte_mux", pll_28nm->id); + clks[num++] = provided_clks[DSI_BYTE_PLL_CLK] = + clk_register_fixed_factor(dev, clk_name, + parent1, CLK_SET_RATE_PARENT, 1, 4); + + pll_28nm->num_clks = num; + + pll_28nm->clk_data.clk_num = NUM_PROVIDED_CLKS; + pll_28nm->clk_data.clks = provided_clks; + + ret = of_clk_add_provider(dev->of_node, + of_clk_src_onecell_get, &pll_28nm->clk_data); + if (ret) { + dev_err(dev, "failed to register clk provider: %d\n", ret); + return ret; + } + + return 0; +} + +struct msm_dsi_pll *msm_dsi_pll_28nm_init(struct platform_device *pdev, + enum msm_dsi_phy_type type, int id) +{ + struct dsi_pll_28nm *pll_28nm; + struct msm_dsi_pll *pll; + int ret; + + if (!pdev) + return ERR_PTR(-ENODEV); + + pll_28nm = devm_kzalloc(&pdev->dev, sizeof(*pll_28nm), GFP_KERNEL); + if (!pll_28nm) + return ERR_PTR(-ENOMEM); + + pll_28nm->pdev = pdev; + pll_28nm->id = id; + + pll_28nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL"); + if (IS_ERR_OR_NULL(pll_28nm->mmio)) { + dev_err(&pdev->dev, "%s: failed to map pll base\n", __func__); + return ERR_PTR(-ENOMEM); + } + + pll = &pll_28nm->base; + pll->min_rate = VCO_MIN_RATE; + pll->max_rate = VCO_MAX_RATE; + pll->get_provider = dsi_pll_28nm_get_provider; + pll->destroy = dsi_pll_28nm_destroy; + pll->disable_seq = dsi_pll_28nm_disable_seq; + pll->save_state = dsi_pll_28nm_save_state; + pll->restore_state = dsi_pll_28nm_restore_state; + + if (type == MSM_DSI_PHY_28NM_HPM) { + pll_28nm->vco_delay = 1; + + pll->en_seq_cnt = 3; + pll->enable_seqs[0] = dsi_pll_28nm_enable_seq_hpm; + pll->enable_seqs[1] = dsi_pll_28nm_enable_seq_hpm; + pll->enable_seqs[2] = dsi_pll_28nm_enable_seq_hpm; + } else if (type == MSM_DSI_PHY_28NM_LP) { + pll_28nm->vco_delay = 1000; + + pll->en_seq_cnt = 1; + pll->enable_seqs[0] = dsi_pll_28nm_enable_seq_lp; + } else { + dev_err(&pdev->dev, "phy type (%d) is not 28nm\n", type); + return ERR_PTR(-EINVAL); + } + + ret = pll_28nm_register(pll_28nm); + if (ret) { + dev_err(&pdev->dev, "failed to register PLL: %d\n", ret); + return ERR_PTR(ret); + } + + return pll; +} + -- cgit v1.2.3-59-g8ed1b From 9d32c4989c858af12b333ae9a3c160a91ff43934 Mon Sep 17 00:00:00 2001 From: Hai Li Date: Fri, 15 May 2015 13:04:05 -0400 Subject: drm/msm/dsi: Enable PLL driver in MSM DSI This change activates PLL driver for DSI to work with common clock framework. Signed-off-by: Hai Li Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/dsi/dsi.c | 14 +++++++ drivers/gpu/drm/msm/dsi/dsi.h | 43 ++++++++++++++++--- drivers/gpu/drm/msm/dsi/dsi_host.c | 60 ++++++++++++++++++++++---- drivers/gpu/drm/msm/dsi/dsi_manager.c | 79 +++++++++++++++++++++++++---------- drivers/gpu/drm/msm/dsi/dsi_phy.c | 68 ++++++++++++++++++++++++++++++ 5 files changed, 229 insertions(+), 35 deletions(-) (limited to 'drivers/gpu/drm/msm/dsi/dsi_phy.c') diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c index ad50b80225f5..beb26dfca276 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.c +++ b/drivers/gpu/drm/msm/dsi/dsi.c @@ -29,6 +29,12 @@ static void dsi_destroy(struct msm_dsi *msm_dsi) return; msm_dsi_manager_unregister(msm_dsi); + + if (msm_dsi->phy) { + msm_dsi_phy_destroy(msm_dsi->phy); + msm_dsi->phy = NULL; + } + if (msm_dsi->host) { msm_dsi_host_destroy(msm_dsi->host); msm_dsi->host = NULL; @@ -63,6 +69,14 @@ static struct msm_dsi *dsi_init(struct platform_device *pdev) if (ret) goto fail; + /* Init dsi PHY */ + msm_dsi->phy = msm_dsi_phy_init(pdev, msm_dsi->phy_type, msm_dsi->id); + if (!msm_dsi->phy) { + ret = -ENXIO; + pr_err("%s: phy init failed\n", __func__); + goto fail; + } + /* Register to dsi manager */ ret = msm_dsi_manager_register(msm_dsi); if (ret) diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 321964a6b27e..8022814f7914 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -38,6 +38,13 @@ #define DSI_ENCODER_MASTER DSI_1 #define DSI_ENCODER_SLAVE DSI_0 +enum msm_dsi_phy_type { + MSM_DSI_PHY_UNKNOWN, + MSM_DSI_PHY_28NM_HPM, + MSM_DSI_PHY_28NM_LP, + MSM_DSI_PHY_MAX +}; + struct msm_dsi { struct drm_device *dev; struct platform_device *pdev; @@ -49,6 +56,8 @@ struct msm_dsi { struct msm_dsi_phy *phy; struct drm_panel *panel; unsigned long panel_flags; + + enum msm_dsi_phy_type phy_type; bool phy_enabled; /* the encoders we are hooked to (outside of dsi block) */ @@ -73,6 +82,29 @@ void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi); /* msm dsi */ struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi); +/* dsi pll */ +struct msm_dsi_pll; +#ifdef CONFIG_DRM_MSM_DSI_PLL +struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, + enum msm_dsi_phy_type type, int dsi_id); +void msm_dsi_pll_destroy(struct msm_dsi_pll *pll); +int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll, + struct clk **byte_clk_provider, struct clk **pixel_clk_provider); +#else +static inline struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, + enum msm_dsi_phy_type type, int id) { + return ERR_PTR(-ENODEV); +} +static inline void msm_dsi_pll_destroy(struct msm_dsi_pll *pll) +{ +} +static inline int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll, + struct clk **byte_clk_provider, struct clk **pixel_clk_provider) +{ + return -ENODEV; +} +#endif + /* dsi host */ int msm_dsi_host_xfer_prepare(struct mipi_dsi_host *host, const struct mipi_dsi_msg *msg); @@ -94,6 +126,8 @@ struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host, unsigned long *panel_flags); int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer); void msm_dsi_host_unregister(struct mipi_dsi_host *host); +int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host, + struct msm_dsi_pll *src_pll); void msm_dsi_host_destroy(struct mipi_dsi_host *host); int msm_dsi_host_modeset_init(struct mipi_dsi_host *host, struct drm_device *dev); @@ -101,18 +135,15 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi); /* dsi phy */ struct msm_dsi_phy; -enum msm_dsi_phy_type { - MSM_DSI_PHY_UNKNOWN, - MSM_DSI_PHY_28NM_HPM, - MSM_DSI_PHY_28NM_LP, - MSM_DSI_PHY_MAX -}; struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, enum msm_dsi_phy_type type, int id); +void msm_dsi_phy_destroy(struct msm_dsi_phy *phy); int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel, const unsigned long bit_rate, const unsigned long esc_rate); int msm_dsi_phy_disable(struct msm_dsi_phy *phy); void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, u32 *clk_pre, u32 *clk_post); +struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy); + #endif /* __DSI_CONNECTOR_H__ */ diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 8a246cfa5d19..43ea8a30c121 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -205,6 +205,9 @@ struct msm_dsi_host { struct clk *byte_clk; struct clk *esc_clk; struct clk *pixel_clk; + struct clk *byte_clk_src; + struct clk *pixel_clk_src; + u32 byte_clk_rate; struct gpio_desc *disp_en_gpio; @@ -463,6 +466,22 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host) goto exit; } + msm_host->byte_clk_src = devm_clk_get(dev, "byte_clk_src"); + if (IS_ERR(msm_host->byte_clk_src)) { + ret = PTR_ERR(msm_host->byte_clk_src); + pr_err("%s: can't find byte_clk_src. ret=%d\n", __func__, ret); + msm_host->byte_clk_src = NULL; + goto exit; + } + + msm_host->pixel_clk_src = devm_clk_get(dev, "pixel_clk_src"); + if (IS_ERR(msm_host->pixel_clk_src)) { + ret = PTR_ERR(msm_host->pixel_clk_src); + pr_err("%s: can't find pixel_clk_src. ret=%d\n", __func__, ret); + msm_host->pixel_clk_src = NULL; + goto exit; + } + exit: return ret; } @@ -1513,15 +1532,9 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) msm_host->workqueue = alloc_ordered_workqueue("dsi_drm_work", 0); INIT_WORK(&msm_host->err_work, dsi_err_worker); - msm_dsi->phy = msm_dsi_phy_init(pdev, msm_host->cfg->phy_type, - msm_host->id); - if (!msm_dsi->phy) { - ret = -EINVAL; - pr_err("%s: phy init failed\n", __func__); - goto fail; - } msm_dsi->host = &msm_host->base; msm_dsi->id = msm_host->id; + msm_dsi->phy_type = msm_host->cfg->phy_type; DBG("Dsi Host %d initialized", msm_host->id); return 0; @@ -1829,6 +1842,39 @@ void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 iova, u32 len) wmb(); } +int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host, + struct msm_dsi_pll *src_pll) +{ + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + struct clk *byte_clk_provider, *pixel_clk_provider; + int ret; + + ret = msm_dsi_pll_get_clk_provider(src_pll, + &byte_clk_provider, &pixel_clk_provider); + if (ret) { + pr_info("%s: can't get provider from pll, don't set parent\n", + __func__); + return 0; + } + + ret = clk_set_parent(msm_host->byte_clk_src, byte_clk_provider); + if (ret) { + pr_err("%s: can't set parent to byte_clk_src. ret=%d\n", + __func__, ret); + goto exit; + } + + ret = clk_set_parent(msm_host->pixel_clk_src, pixel_clk_provider); + if (ret) { + pr_err("%s: can't set parent to pixel_clk_src. ret=%d\n", + __func__, ret); + goto exit; + } + +exit: + return ret; +} + int msm_dsi_host_enable(struct mipi_dsi_host *host) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 0a40f3c64e8b..87ac6612b6f8 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -60,6 +60,53 @@ static int dsi_mgr_parse_dual_panel(struct device_node *np, int id) return 0; } +static int dsi_mgr_host_register(int id) +{ + struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); + struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); + struct msm_dsi *clk_master_dsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); + struct msm_dsi_pll *src_pll; + int ret; + + if (!IS_DUAL_PANEL()) { + ret = msm_dsi_host_register(msm_dsi->host, true); + if (ret) + return ret; + + src_pll = msm_dsi_phy_get_pll(msm_dsi->phy); + ret = msm_dsi_host_set_src_pll(msm_dsi->host, src_pll); + } else if (!other_dsi) { + ret = 0; + } else { + struct msm_dsi *mdsi = IS_MASTER_PANEL(id) ? + msm_dsi : other_dsi; + struct msm_dsi *sdsi = IS_MASTER_PANEL(id) ? + other_dsi : msm_dsi; + /* Register slave host first, so that slave DSI device + * has a chance to probe, and do not block the master + * DSI device's probe. + * Also, do not check defer for the slave host, + * because only master DSI device adds the panel to global + * panel list. The panel's device is the master DSI device. + */ + ret = msm_dsi_host_register(sdsi->host, false); + if (ret) + return ret; + ret = msm_dsi_host_register(mdsi->host, true); + if (ret) + return ret; + + /* PLL0 is to drive both 2 DSI link clocks in Dual DSI mode. */ + src_pll = msm_dsi_phy_get_pll(clk_master_dsi->phy); + ret = msm_dsi_host_set_src_pll(msm_dsi->host, src_pll); + if (ret) + return ret; + ret = msm_dsi_host_set_src_pll(other_dsi->host, src_pll); + } + + return ret; +} + struct dsi_connector { struct drm_connector base; int id; @@ -652,7 +699,6 @@ int msm_dsi_manager_register(struct msm_dsi *msm_dsi) { struct msm_dsi_manager *msm_dsim = &msm_dsim_glb; int id = msm_dsi->id; - struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); int ret; if (id > DSI_MAX) { @@ -670,31 +716,20 @@ int msm_dsi_manager_register(struct msm_dsi *msm_dsi) ret = dsi_mgr_parse_dual_panel(msm_dsi->pdev->dev.of_node, id); if (ret) { pr_err("%s: failed to parse dual panel info\n", __func__); - return ret; + goto fail; } - if (!IS_DUAL_PANEL()) { - ret = msm_dsi_host_register(msm_dsi->host, true); - } else if (!other_dsi) { - return 0; - } else { - struct msm_dsi *mdsi = IS_MASTER_PANEL(id) ? - msm_dsi : other_dsi; - struct msm_dsi *sdsi = IS_MASTER_PANEL(id) ? - other_dsi : msm_dsi; - /* Register slave host first, so that slave DSI device - * has a chance to probe, and do not block the master - * DSI device's probe. - * Also, do not check defer for the slave host, - * because only master DSI device adds the panel to global - * panel list. The panel's device is the master DSI device. - */ - ret = msm_dsi_host_register(sdsi->host, false); - if (ret) - return ret; - ret = msm_dsi_host_register(mdsi->host, true); + ret = dsi_mgr_host_register(id); + if (ret) { + pr_err("%s: failed to register mipi dsi host for DSI %d\n", + __func__, id); + goto fail; } + return 0; + +fail: + msm_dsim->dsi[id] = NULL; return ret; } diff --git a/drivers/gpu/drm/msm/dsi/dsi_phy.c b/drivers/gpu/drm/msm/dsi/dsi_phy.c index 4403f38bf220..2b1c8fdb10de 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/dsi_phy.c @@ -34,10 +34,18 @@ struct dsi_dphy_timing { }; struct msm_dsi_phy { + struct platform_device *pdev; void __iomem *base; void __iomem *reg_base; int id; + + struct clk *ahb_clk; + struct dsi_dphy_timing timing; + enum msm_dsi_phy_type type; + + struct msm_dsi_pll *pll; + int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel, const unsigned long bit_rate, const unsigned long esc_rate); int (*disable)(struct msm_dsi_phy *phy); @@ -284,6 +292,27 @@ static int dsi_28nm_phy_disable(struct msm_dsi_phy *phy) return 0; } +static int dsi_phy_enable_resource(struct msm_dsi_phy *phy) +{ + int ret; + + pm_runtime_get_sync(&phy->pdev->dev); + + ret = clk_prepare_enable(phy->ahb_clk); + if (ret) { + pr_err("%s: can't enable ahb clk, %d\n", __func__, ret); + pm_runtime_put_sync(&phy->pdev->dev); + } + + return ret; +} + +static void dsi_phy_disable_resource(struct msm_dsi_phy *phy) +{ + clk_disable_unprepare(phy->ahb_clk); + pm_runtime_put_sync(&phy->pdev->dev); +} + #define dsi_phy_func_init(name) \ do { \ phy->enable = dsi_##name##_phy_enable; \ @@ -294,6 +323,7 @@ struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, enum msm_dsi_phy_type type, int id) { struct msm_dsi_phy *phy; + int ret; phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); if (!phy) @@ -320,11 +350,41 @@ struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, return NULL; } + phy->type = type; phy->id = id; + phy->pdev = pdev; + + phy->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk"); + if (IS_ERR(phy->ahb_clk)) { + pr_err("%s: Unable to get ahb clk\n", __func__); + return NULL; + } + + /* PLL init will call into clk_register which requires + * register access, so we need to enable power and ahb clock. + */ + ret = dsi_phy_enable_resource(phy); + if (ret) + return NULL; + + phy->pll = msm_dsi_pll_init(pdev, type, id); + if (!phy->pll) + pr_info("%s: pll init failed, need separate pll clk driver\n", + __func__); + + dsi_phy_disable_resource(phy); return phy; } +void msm_dsi_phy_destroy(struct msm_dsi_phy *phy) +{ + if (phy->pll) { + msm_dsi_pll_destroy(phy->pll); + phy->pll = NULL; + } +} + int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel, const unsigned long bit_rate, const unsigned long esc_rate) { @@ -351,3 +411,11 @@ void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, *clk_post = phy->timing.clk_post; } +struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy) +{ + if (!phy) + return NULL; + + return phy->pll; +} + -- cgit v1.2.3-59-g8ed1b From ec31abf6684ebe1134eb3320c96fb92e566eff74 Mon Sep 17 00:00:00 2001 From: Hai Li Date: Fri, 15 May 2015 13:04:06 -0400 Subject: drm/msm/dsi: Separate PHY to another platform device There are different types of PHY from one chipset to another, while the DSI host controller is relatively consistent across platforms. Also, the PLL inside PHY is providing the source of DSI byte and pixel clocks, which are used by DSI host controller. Separated devices for clock provider and clock consumer make DSI driver better fit into common clock framework. Signed-off-by: Hai Li Signed-off-by: Rob Clark --- Documentation/devicetree/bindings/drm/msm/dsi.txt | 59 +++-- drivers/gpu/drm/msm/dsi/dsi.c | 45 +++- drivers/gpu/drm/msm/dsi/dsi.h | 25 +- drivers/gpu/drm/msm/dsi/dsi_host.c | 25 +- drivers/gpu/drm/msm/dsi/dsi_phy.c | 266 ++++++++++++++++++---- 5 files changed, 326 insertions(+), 94 deletions(-) (limited to 'drivers/gpu/drm/msm/dsi/dsi_phy.c') diff --git a/Documentation/devicetree/bindings/drm/msm/dsi.txt b/Documentation/devicetree/bindings/drm/msm/dsi.txt index ff8aebb2f8f8..cd8fe6cf536c 100644 --- a/Documentation/devicetree/bindings/drm/msm/dsi.txt +++ b/Documentation/devicetree/bindings/drm/msm/dsi.txt @@ -1,15 +1,12 @@ Qualcomm Technologies Inc. adreno/snapdragon DSI output +DSI Controller: Required properties: - compatible: * "qcom,mdss-dsi-ctrl" -- reg: Physical base address and length of the registers of controller, PLL, - PHY and PHY regulator +- reg: Physical base address and length of the registers of controller - reg-names: The names of register regions. The following regions are required: * "dsi_ctrl" - * "dsi_pll" - * "dsi_phy" - * "dsi_phy_regulator" - qcom,dsi-host-index: The ID of DSI controller hardware instance. This should be 0 or 1, since we have 2 DSI controllers at most for now. - interrupts: The interrupt signal from the DSI block. @@ -24,10 +21,10 @@ Required properties: * "iface_clk" * "mdp_core_clk" * "pixel_clk" -- #clock-cells: The value should be 1. - vdd-supply: phandle to vdd regulator device node - vddio-supply: phandle to vdd-io regulator device node - vdda-supply: phandle to vdda regulator device node +- qcom,dsi-phy: phandle to DSI PHY device node Optional properties: - panel@0: Node of panel connected to this DSI controller. @@ -42,22 +39,34 @@ Optional properties: - interrupt-parent: phandle to the MDP block if the interrupt signal is routed through MDP block +DSI PHY: +Required properties: +- compatible: Could be the following + * "qcom,dsi-phy-28nm-hpm" + * "qcom,dsi-phy-28nm-lp" +- reg: Physical base address and length of the registers of PLL, PHY and PHY + regulator +- reg-names: The names of register regions. The following regions are required: + * "dsi_pll" + * "dsi_phy" + * "dsi_phy_regulator" +- qcom,dsi-phy-index: The ID of DSI PHY hardware instance. This should + be 0 or 1, since we have 2 DSI PHYs at most for now. +- power-domains: Should be <&mmcc MDSS_GDSC>. +- clocks: device clocks + See Documentation/devicetree/bindings/clocks/clock-bindings.txt for details. +- clock-names: the following clocks are required: + * "iface_clk" +- vddio-supply: phandle to vdd-io regulator device node + Example: mdss_dsi0: qcom,mdss_dsi@fd922800 { compatible = "qcom,mdss-dsi-ctrl"; qcom,dsi-host-index = <0>; interrupt-parent = <&mdss_mdp>; interrupts = <4 0>; - reg-names = - "dsi_ctrl", - "dsi_pll", - "dsi_phy", - "dsi_phy_regulator", - reg = <0xfd922800 0x200>, - <0xfd922a00 0xd4>, - <0xfd922b00 0x2b0>, - <0xfd922d80 0x7b>, - <0xfd828000 0x108>; + reg-names = "dsi_ctrl"; + reg = <0xfd922800 0x200>; power-domains = <&mmcc MDSS_GDSC>; clock-names = "bus_clk", @@ -75,11 +84,12 @@ Example: <&mmcc MDSS_AHB_CLK>, <&mmcc MDSS_MDP_CLK>, <&mmcc MDSS_PCLK0_CLK>; - #clock-cells = <1>; vdda-supply = <&pma8084_l2>; vdd-supply = <&pma8084_l22>; vddio-supply = <&pma8084_l12>; + qcom,dsi-phy = <&mdss_dsi_phy0>; + qcom,dual-panel-mode; qcom,master-panel; qcom,sync-dual-panel; @@ -93,3 +103,18 @@ Example: backlight = <...>; }; }; + + mdss_dsi_phy0: qcom,mdss_dsi_phy@fd922a00 { + compatible = "qcom,dsi-phy-28nm-hpm"; + qcom,dsi-phy-index = <0>; + reg-names = + "dsi_pll", + "dsi_phy", + "dsi_phy_regulator"; + reg = <0xfd922a00 0xd4>, + <0xfd922b00 0x2b0>, + <0xfd922d80 0x7b>; + clock-names = "iface_clk"; + clocks = <&mmcc MDSS_AHB_CLK>; + vddio-supply = <&pma8084_l12>; + }; diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c index beb26dfca276..1f2561e2ff71 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.c +++ b/drivers/gpu/drm/msm/dsi/dsi.c @@ -23,6 +23,34 @@ struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi) msm_dsi->encoders[MSM_DSI_CMD_ENCODER_ID]; } +static int dsi_get_phy(struct msm_dsi *msm_dsi) +{ + struct platform_device *pdev = msm_dsi->pdev; + struct platform_device *phy_pdev; + struct device_node *phy_node; + + phy_node = of_parse_phandle(pdev->dev.of_node, "qcom,dsi-phy", 0); + if (!phy_node) { + dev_err(&pdev->dev, "cannot find phy device\n"); + return -ENXIO; + } + + phy_pdev = of_find_device_by_node(phy_node); + if (phy_pdev) + msm_dsi->phy = platform_get_drvdata(phy_pdev); + + of_node_put(phy_node); + + if (!phy_pdev || !msm_dsi->phy) { + dev_err(&pdev->dev, "%s: phy driver is not ready\n", __func__); + return -EPROBE_DEFER; + } + + msm_dsi->phy_dev = get_device(&phy_pdev->dev); + + return 0; +} + static void dsi_destroy(struct msm_dsi *msm_dsi) { if (!msm_dsi) @@ -30,9 +58,10 @@ static void dsi_destroy(struct msm_dsi *msm_dsi) msm_dsi_manager_unregister(msm_dsi); - if (msm_dsi->phy) { - msm_dsi_phy_destroy(msm_dsi->phy); + if (msm_dsi->phy_dev) { + put_device(msm_dsi->phy_dev); msm_dsi->phy = NULL; + msm_dsi->phy_dev = NULL; } if (msm_dsi->host) { @@ -49,7 +78,6 @@ static struct msm_dsi *dsi_init(struct platform_device *pdev) int ret; if (!pdev) { - dev_err(&pdev->dev, "no dsi device\n"); ret = -ENXIO; goto fail; } @@ -69,13 +97,10 @@ static struct msm_dsi *dsi_init(struct platform_device *pdev) if (ret) goto fail; - /* Init dsi PHY */ - msm_dsi->phy = msm_dsi_phy_init(pdev, msm_dsi->phy_type, msm_dsi->id); - if (!msm_dsi->phy) { - ret = -ENXIO; - pr_err("%s: phy init failed\n", __func__); + /* GET dsi PHY */ + ret = dsi_get_phy(msm_dsi); + if (ret) goto fail; - } /* Register to dsi manager */ ret = msm_dsi_manager_register(msm_dsi); @@ -156,12 +181,14 @@ static struct platform_driver dsi_driver = { void __init msm_dsi_register(void) { DBG(""); + msm_dsi_phy_driver_register(); platform_driver_register(&dsi_driver); } void __exit msm_dsi_unregister(void) { DBG(""); + msm_dsi_phy_driver_unregister(); platform_driver_unregister(&dsi_driver); } diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 8022814f7914..92d697de4858 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -14,6 +14,7 @@ #ifndef __DSI_CONNECTOR_H__ #define __DSI_CONNECTOR_H__ +#include #include #include "drm_crtc.h" @@ -39,12 +40,27 @@ #define DSI_ENCODER_SLAVE DSI_0 enum msm_dsi_phy_type { - MSM_DSI_PHY_UNKNOWN, MSM_DSI_PHY_28NM_HPM, MSM_DSI_PHY_28NM_LP, MSM_DSI_PHY_MAX }; +#define DSI_DEV_REGULATOR_MAX 8 + +/* Regulators for DSI devices */ +struct dsi_reg_entry { + char name[32]; + int min_voltage; + int max_voltage; + int enable_load; + int disable_load; +}; + +struct dsi_reg_config { + int num; + struct dsi_reg_entry regs[DSI_DEV_REGULATOR_MAX]; +}; + struct msm_dsi { struct drm_device *dev; struct platform_device *pdev; @@ -57,7 +73,7 @@ struct msm_dsi { struct drm_panel *panel; unsigned long panel_flags; - enum msm_dsi_phy_type phy_type; + struct device *phy_dev; bool phy_enabled; /* the encoders we are hooked to (outside of dsi block) */ @@ -135,9 +151,8 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi); /* dsi phy */ struct msm_dsi_phy; -struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, - enum msm_dsi_phy_type type, int id); -void msm_dsi_phy_destroy(struct msm_dsi_phy *phy); +void msm_dsi_phy_driver_register(void); +void msm_dsi_phy_driver_unregister(void); int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel, const unsigned long bit_rate, const unsigned long esc_rate); int msm_dsi_phy_disable(struct msm_dsi_phy *phy); diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 43ea8a30c121..bc2e405758e3 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -36,35 +36,19 @@ #define DSI_6G_REG_SHIFT 4 -#define DSI_REGULATOR_MAX 8 -struct dsi_reg_entry { - char name[32]; - int min_voltage; - int max_voltage; - int enable_load; - int disable_load; -}; - -struct dsi_reg_config { - int num; - struct dsi_reg_entry regs[DSI_REGULATOR_MAX]; -}; - struct dsi_config { u32 major; u32 minor; u32 io_offset; - enum msm_dsi_phy_type phy_type; struct dsi_reg_config reg_cfg; }; static const struct dsi_config dsi_cfgs[] = { - {MSM_DSI_VER_MAJOR_V2, 0, 0, MSM_DSI_PHY_UNKNOWN}, + {MSM_DSI_VER_MAJOR_V2, 0, 0, {0,} }, { /* 8974 v1 */ .major = MSM_DSI_VER_MAJOR_6G, .minor = MSM_DSI_6G_VER_MINOR_V1_0, .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM_HPM, .reg_cfg = { .num = 4, .regs = { @@ -79,7 +63,6 @@ static const struct dsi_config dsi_cfgs[] = { .major = MSM_DSI_VER_MAJOR_6G, .minor = MSM_DSI_6G_VER_MINOR_V1_1, .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM_HPM, .reg_cfg = { .num = 4, .regs = { @@ -94,7 +77,6 @@ static const struct dsi_config dsi_cfgs[] = { .major = MSM_DSI_VER_MAJOR_6G, .minor = MSM_DSI_6G_VER_MINOR_V1_1_1, .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM_HPM, .reg_cfg = { .num = 4, .regs = { @@ -109,7 +91,6 @@ static const struct dsi_config dsi_cfgs[] = { .major = MSM_DSI_VER_MAJOR_6G, .minor = MSM_DSI_6G_VER_MINOR_V1_2, .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM_HPM, .reg_cfg = { .num = 4, .regs = { @@ -124,7 +105,6 @@ static const struct dsi_config dsi_cfgs[] = { .major = MSM_DSI_VER_MAJOR_6G, .minor = MSM_DSI_6G_VER_MINOR_V1_3_1, .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM_LP, .reg_cfg = { .num = 4, .regs = { @@ -197,7 +177,7 @@ struct msm_dsi_host { int id; void __iomem *ctrl_base; - struct regulator_bulk_data supplies[DSI_REGULATOR_MAX]; + struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX]; struct clk *mdp_core_clk; struct clk *ahb_clk; struct clk *axi_clk; @@ -1534,7 +1514,6 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) msm_dsi->host = &msm_host->base; msm_dsi->id = msm_host->id; - msm_dsi->phy_type = msm_host->cfg->phy_type; DBG("Dsi Host %d initialized", msm_host->id); return 0; diff --git a/drivers/gpu/drm/msm/dsi/dsi_phy.c b/drivers/gpu/drm/msm/dsi/dsi_phy.c index 2b1c8fdb10de..2d3b33ce1cc5 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi/dsi_phy.c @@ -11,12 +11,27 @@ * GNU General Public License for more details. */ +#include +#include + #include "dsi.h" #include "dsi.xml.h" #define dsi_phy_read(offset) msm_readl((offset)) #define dsi_phy_write(offset, data) msm_writel((data), (offset)) +struct dsi_phy_ops { + int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel, + const unsigned long bit_rate, const unsigned long esc_rate); + int (*disable)(struct msm_dsi_phy *phy); +}; + +struct dsi_phy_cfg { + enum msm_dsi_phy_type type; + struct dsi_reg_config reg_cfg; + struct dsi_phy_ops ops; +}; + struct dsi_dphy_timing { u32 clk_pre; u32 clk_post; @@ -40,17 +55,100 @@ struct msm_dsi_phy { int id; struct clk *ahb_clk; + struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX]; struct dsi_dphy_timing timing; - enum msm_dsi_phy_type type; + const struct dsi_phy_cfg *cfg; struct msm_dsi_pll *pll; - - int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel, - const unsigned long bit_rate, const unsigned long esc_rate); - int (*disable)(struct msm_dsi_phy *phy); }; +static int dsi_phy_regulator_init(struct msm_dsi_phy *phy) +{ + struct regulator_bulk_data *s = phy->supplies; + const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs; + struct device *dev = &phy->pdev->dev; + int num = phy->cfg->reg_cfg.num; + int i, ret; + + for (i = 0; i < num; i++) + s[i].supply = regs[i].name; + + ret = devm_regulator_bulk_get(&phy->pdev->dev, num, s); + if (ret < 0) { + dev_err(dev, "%s: failed to init regulator, ret=%d\n", + __func__, ret); + return ret; + } + + for (i = 0; i < num; i++) { + if ((regs[i].min_voltage >= 0) && (regs[i].max_voltage >= 0)) { + ret = regulator_set_voltage(s[i].consumer, + regs[i].min_voltage, regs[i].max_voltage); + if (ret < 0) { + dev_err(dev, + "regulator %d set voltage failed, %d\n", + i, ret); + return ret; + } + } + } + + return 0; +} + +static void dsi_phy_regulator_disable(struct msm_dsi_phy *phy) +{ + struct regulator_bulk_data *s = phy->supplies; + const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs; + int num = phy->cfg->reg_cfg.num; + int i; + + DBG(""); + for (i = num - 1; i >= 0; i--) + if (regs[i].disable_load >= 0) + regulator_set_load(s[i].consumer, + regs[i].disable_load); + + regulator_bulk_disable(num, s); +} + +static int dsi_phy_regulator_enable(struct msm_dsi_phy *phy) +{ + struct regulator_bulk_data *s = phy->supplies; + const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs; + struct device *dev = &phy->pdev->dev; + int num = phy->cfg->reg_cfg.num; + int ret, i; + + DBG(""); + for (i = 0; i < num; i++) { + if (regs[i].enable_load >= 0) { + ret = regulator_set_load(s[i].consumer, + regs[i].enable_load); + if (ret < 0) { + dev_err(dev, + "regulator %d set op mode failed, %d\n", + i, ret); + goto fail; + } + } + } + + ret = regulator_bulk_enable(num, s); + if (ret < 0) { + dev_err(dev, "regulator enable failed, %d\n", ret); + goto fail; + } + + return 0; + +fail: + for (i--; i >= 0; i--) + regulator_set_load(s[i].consumer, regs[i].disable_load); + return ret; +} + #define S_DIV_ROUND_UP(n, d) \ (((n) >= 0) ? (((n) + (d) - 1) / (d)) : (((n) - (d) + 1) / (d))) @@ -313,51 +411,94 @@ static void dsi_phy_disable_resource(struct msm_dsi_phy *phy) pm_runtime_put_sync(&phy->pdev->dev); } -#define dsi_phy_func_init(name) \ - do { \ - phy->enable = dsi_##name##_phy_enable; \ - phy->disable = dsi_##name##_phy_disable; \ - } while (0) +static const struct dsi_phy_cfg dsi_phy_cfgs[MSM_DSI_PHY_MAX] = { + [MSM_DSI_PHY_28NM_HPM] = { + .type = MSM_DSI_PHY_28NM_HPM, + .reg_cfg = { + .num = 1, + .regs = { + {"vddio", 1800000, 1800000, 100000, 100}, + }, + }, + .ops = { + .enable = dsi_28nm_phy_enable, + .disable = dsi_28nm_phy_disable, + } + }, + [MSM_DSI_PHY_28NM_LP] = { + .type = MSM_DSI_PHY_28NM_LP, + .reg_cfg = { + .num = 1, + .regs = { + {"vddio", 1800000, 1800000, 100000, 100}, + }, + }, + .ops = { + .enable = dsi_28nm_phy_enable, + .disable = dsi_28nm_phy_disable, + } + }, +}; + +static const struct of_device_id dsi_phy_dt_match[] = { + { .compatible = "qcom,dsi-phy-28nm-hpm", + .data = &dsi_phy_cfgs[MSM_DSI_PHY_28NM_HPM],}, + { .compatible = "qcom,dsi-phy-28nm-lp", + .data = &dsi_phy_cfgs[MSM_DSI_PHY_28NM_LP],}, + {} +}; -struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, - enum msm_dsi_phy_type type, int id) +static int dsi_phy_driver_probe(struct platform_device *pdev) { struct msm_dsi_phy *phy; + const struct of_device_id *match; int ret; phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); if (!phy) - return NULL; + return -ENOMEM; + + match = of_match_node(dsi_phy_dt_match, pdev->dev.of_node); + if (!match) + return -ENODEV; + + phy->cfg = match->data; + phy->pdev = pdev; + + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,dsi-phy-index", &phy->id); + if (ret) { + dev_err(&pdev->dev, + "%s: PHY index not specified, ret=%d\n", + __func__, ret); + goto fail; + } phy->base = msm_ioremap(pdev, "dsi_phy", "DSI_PHY"); if (IS_ERR(phy->base)) { - pr_err("%s: failed to map phy base\n", __func__); - return NULL; + dev_err(&pdev->dev, "%s: failed to map phy base\n", __func__); + ret = -ENOMEM; + goto fail; } phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", "DSI_PHY_REG"); if (IS_ERR(phy->reg_base)) { - pr_err("%s: failed to map phy regulator base\n", __func__); - return NULL; + dev_err(&pdev->dev, + "%s: failed to map phy regulator base\n", __func__); + ret = -ENOMEM; + goto fail; } - switch (type) { - case MSM_DSI_PHY_28NM_HPM: - case MSM_DSI_PHY_28NM_LP: - dsi_phy_func_init(28nm); - break; - default: - pr_err("%s: unsupported type, %d\n", __func__, type); - return NULL; + ret = dsi_phy_regulator_init(phy); + if (ret) { + dev_err(&pdev->dev, "%s: failed to init regulator\n", __func__); + goto fail; } - phy->type = type; - phy->id = id; - phy->pdev = pdev; - phy->ahb_clk = devm_clk_get(&pdev->dev, "iface_clk"); if (IS_ERR(phy->ahb_clk)) { pr_err("%s: Unable to get ahb clk\n", __func__); - return NULL; + ret = PTR_ERR(phy->ahb_clk); + goto fail; } /* PLL init will call into clk_register which requires @@ -365,39 +506,84 @@ struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, */ ret = dsi_phy_enable_resource(phy); if (ret) - return NULL; + goto fail; - phy->pll = msm_dsi_pll_init(pdev, type, id); + phy->pll = msm_dsi_pll_init(pdev, phy->cfg->type, phy->id); if (!phy->pll) - pr_info("%s: pll init failed, need separate pll clk driver\n", + dev_info(&pdev->dev, + "%s: pll init failed, need separate pll clk driver\n", __func__); dsi_phy_disable_resource(phy); - return phy; + platform_set_drvdata(pdev, phy); + + return 0; + +fail: + return ret; } -void msm_dsi_phy_destroy(struct msm_dsi_phy *phy) +static int dsi_phy_driver_remove(struct platform_device *pdev) { - if (phy->pll) { + struct msm_dsi_phy *phy = platform_get_drvdata(pdev); + + if (phy && phy->pll) { msm_dsi_pll_destroy(phy->pll); phy->pll = NULL; } + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver dsi_phy_platform_driver = { + .probe = dsi_phy_driver_probe, + .remove = dsi_phy_driver_remove, + .driver = { + .name = "msm_dsi_phy", + .of_match_table = dsi_phy_dt_match, + }, +}; + +void __init msm_dsi_phy_driver_register(void) +{ + platform_driver_register(&dsi_phy_platform_driver); +} + +void __exit msm_dsi_phy_driver_unregister(void) +{ + platform_driver_unregister(&dsi_phy_platform_driver); } int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel, const unsigned long bit_rate, const unsigned long esc_rate) { - if (!phy || !phy->enable) + int ret; + + if (!phy || !phy->cfg->ops.enable) return -EINVAL; - return phy->enable(phy, is_dual_panel, bit_rate, esc_rate); + + ret = dsi_phy_regulator_enable(phy); + if (ret) { + dev_err(&phy->pdev->dev, "%s: regulator enable failed, %d\n", + __func__, ret); + return ret; + } + + return phy->cfg->ops.enable(phy, is_dual_panel, bit_rate, esc_rate); } int msm_dsi_phy_disable(struct msm_dsi_phy *phy) { - if (!phy || !phy->disable) + if (!phy || !phy->cfg->ops.disable) return -EINVAL; - return phy->disable(phy); + + phy->cfg->ops.disable(phy); + dsi_phy_regulator_disable(phy); + + return 0; } void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, -- cgit v1.2.3-59-g8ed1b