From e6c4c78c10d87a99f298a2455a4aaede5743bb4f Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Mon, 30 Nov 2015 17:47:17 +0530 Subject: drm/msm/dsi: Don't get byte/pixel source clocks from DT We retrieve the byte and pixel source clocks (RCG clocks) in the dsi driver via DT. These are needed so that we can re-parent these source clocks if we want to drive it using a different DSI PLL. We shouldn't get these via DT because they aren't clocks that directly serve as inputs to the dsi host. Fortunately, there is a static parent-child link between the byte_clk_src/pixel_clk_src and byte_clk/pixel_clk clocks. So, we can retrieve the source clocks via clk_get_parent. Do this instead of retrieving via DT. Cc: Rob Herring Cc: Stephen Boyd Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/dsi/dsi_host.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm/msm/dsi/dsi_host.c') diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 4c49868efcda..aec97c8e5ac4 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -356,20 +356,17 @@ 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); + msm_host->byte_clk_src = clk_get_parent(msm_host->byte_clk); + if (!msm_host->byte_clk_src) { + ret = -ENODEV; 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); + msm_host->pixel_clk_src = clk_get_parent(msm_host->pixel_clk); + if (!msm_host->pixel_clk_src) { + ret = -ENODEV; pr_err("%s: can't find pixel_clk_src. ret=%d\n", __func__, ret); - msm_host->pixel_clk_src = NULL; - goto exit; } exit: -- cgit v1.2.3 From 648d5063727ae3f555317baf6a5d2dc1e8e8d2a5 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Fri, 9 Oct 2015 11:10:59 +0530 Subject: drm/msm/dsi: Use a better way to figure out DSI version The current version checking mechanism works fine for DSI6G blocks. It doesn't work so well for older generation DSIv2 blocks. The initial read of REG_DSI_6G_HW_VERSION(offset 0x0) would result in a read of REG_DSI_CTRL for DSIv2. This register won't necessarily be 0 on DSIv2. It can be non zero if DSI was previously initialized by the bootloader. Instead of reading offset 0x0, we now read offset 0x1f0. For DSIv2, this register is DSI_VERSION, and is bound to be non-zero. On DSI6G, this register(offset 0x1f0) is SCRATCH_REGISTER_0, which no one ever seems to touch, and from all register dumps I'vc seen, holds 0 all the time. Modify dsi_get_version to read REG_DSI_VERSION to determine whether we are DSI6G or DSIv2. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/dsi/dsi_host.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/msm/dsi/dsi_host.c') diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index aec97c8e5ac4..7a365df91c53 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -33,17 +33,24 @@ static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor) { u32 ver; - u32 ver_6g; if (!major || !minor) return -EINVAL; - /* From DSI6G(v3), addition of a 6G_HW_VERSION register at offset 0 + /* + * From DSI6G(v3), addition of a 6G_HW_VERSION register at offset 0 * makes all other registers 4-byte shifted down. + * + * In order to identify between DSI6G(v3) and beyond, and DSIv2 and + * older, we read the DSI_VERSION register without any shift(offset + * 0x1f0). In the case of DSIv2, this hast to be a non-zero value. In + * the case of DSI6G, this has to be zero (the offset points to a + * scratch register which we never touch) */ - ver_6g = msm_readl(base + REG_DSI_6G_HW_VERSION); - if (ver_6g == 0) { - ver = msm_readl(base + REG_DSI_VERSION); + + ver = msm_readl(base + REG_DSI_VERSION); + if (ver) { + /* older dsi host, there is no register shift */ ver = FIELD(ver, DSI_VERSION_MAJOR); if (ver <= MSM_DSI_VER_MAJOR_V2) { /* old versions */ @@ -54,12 +61,17 @@ static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor) return -EINVAL; } } else { + /* + * newer host, offset 0 has 6G_HW_VERSION, the rest of the + * registers are shifted down, read DSI_VERSION again with + * the shifted offset + */ ver = msm_readl(base + DSI_6G_REG_SHIFT + REG_DSI_VERSION); ver = FIELD(ver, DSI_VERSION_MAJOR); if (ver == MSM_DSI_VER_MAJOR_6G) { /* 6G version */ *major = ver; - *minor = ver_6g; + *minor = msm_readl(base + REG_DSI_6G_HW_VERSION); return 0; } else { return -EINVAL; -- cgit v1.2.3 From 31c92767aef63501da0214da9736cf60ac684f20 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Fri, 9 Oct 2015 12:40:39 +0530 Subject: drm/msm/dsi: Delay dsi_clk_init Initialize clocks only after we get the DSI host version. This will allow us to get clocks using a pre-defined list based on the DSI major/minor version of the host. This is required since clock requirements of different major DSI revisions(v2 vs 6g) aren't the same. Modify dsi_get_version to get the interface clock, and then put it after it is used. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/dsi/dsi_host.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm/msm/dsi/dsi_host.c') diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 7a365df91c53..f78702472819 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -177,21 +177,31 @@ static const struct msm_dsi_cfg_handler *dsi_get_config( struct msm_dsi_host *msm_host) { const struct msm_dsi_cfg_handler *cfg_hnd = NULL; + struct device *dev = &msm_host->pdev->dev; struct regulator *gdsc_reg; + struct clk *ahb_clk; int ret; u32 major = 0, minor = 0; - gdsc_reg = regulator_get(&msm_host->pdev->dev, "gdsc"); + gdsc_reg = regulator_get(dev, "gdsc"); if (IS_ERR(gdsc_reg)) { pr_err("%s: cannot get gdsc\n", __func__); goto exit; } + + ahb_clk = clk_get(dev, "iface_clk"); + if (IS_ERR(ahb_clk)) { + pr_err("%s: cannot get interface clock\n", __func__); + goto put_gdsc; + } + ret = regulator_enable(gdsc_reg); if (ret) { pr_err("%s: unable to enable gdsc\n", __func__); - goto put_gdsc; + goto put_clk; } - ret = clk_prepare_enable(msm_host->ahb_clk); + + ret = clk_prepare_enable(ahb_clk); if (ret) { pr_err("%s: unable to enable ahb_clk\n", __func__); goto disable_gdsc; @@ -208,9 +218,11 @@ static const struct msm_dsi_cfg_handler *dsi_get_config( DBG("%s: Version %x:%x\n", __func__, major, minor); disable_clks: - clk_disable_unprepare(msm_host->ahb_clk); + clk_disable_unprepare(ahb_clk); disable_gdsc: regulator_disable(gdsc_reg); +put_clk: + clk_put(ahb_clk); put_gdsc: regulator_put(gdsc_reg); exit: @@ -1417,12 +1429,6 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) goto fail; } - ret = dsi_clk_init(msm_host); - if (ret) { - pr_err("%s: unable to initialize dsi clks\n", __func__); - goto fail; - } - msm_host->ctrl_base = msm_ioremap(pdev, "dsi_ctrl", "DSI CTRL"); if (IS_ERR(msm_host->ctrl_base)) { pr_err("%s: unable to map Dsi ctrl base\n", __func__); @@ -1446,6 +1452,12 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) goto fail; } + ret = dsi_clk_init(msm_host); + if (ret) { + pr_err("%s: unable to initialize dsi clks\n", __func__); + goto fail; + } + msm_host->rx_buf = devm_kzalloc(&pdev->dev, SZ_4K, GFP_KERNEL); if (!msm_host->rx_buf) { pr_err("%s: alloc rx temp buf failed\n", __func__); -- cgit v1.2.3 From 6e0eb52eba9e2c8d56e4e6826faa2bd6fd5dcf0c Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Fri, 9 Oct 2015 15:21:12 +0530 Subject: drm/msm/dsi: Parse bus clocks from a list DSI bus clocks seem to vary between different DSI host versions, and the SOC to which they belong. Even the enable/disable sequence varies. Provide a list of bus clock names in dsi_cfg. The driver will use this to retrieve the clocks, and enable/disable them. Add bus clock lists for DSI6G, and DSI for MSM8916(this is DSI6G too, but there is no MMSS_CC specific clock since there is no MMSS clock controller on 8916). Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/dsi/dsi.h | 1 + drivers/gpu/drm/msm/dsi/dsi_cfg.c | 16 +++++- drivers/gpu/drm/msm/dsi/dsi_cfg.h | 2 + drivers/gpu/drm/msm/dsi/dsi_host.c | 107 ++++++++++++------------------------- 4 files changed, 53 insertions(+), 73 deletions(-) (limited to 'drivers/gpu/drm/msm/dsi/dsi_host.c') diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 8140e8b820e4..7336b55d05cc 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -36,6 +36,7 @@ enum msm_dsi_phy_type { }; #define DSI_DEV_REGULATOR_MAX 8 +#define DSI_BUS_CLK_MAX 4 /* Regulators for DSI devices */ struct dsi_reg_entry { diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.c b/drivers/gpu/drm/msm/dsi/dsi_cfg.c index 5872d5e5934f..c40044f8a6c0 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.c +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.c @@ -18,6 +18,10 @@ static const struct msm_dsi_config dsi_v2_cfg = { .io_offset = 0, }; +static const char * const dsi_6g_bus_clk_names[] = { + "mdp_core_clk", "iface_clk", "bus_clk", "core_mmss_clk", +}; + static const struct msm_dsi_config msm8974_apq8084_dsi_cfg = { .io_offset = DSI_6G_REG_SHIFT, .reg_cfg = { @@ -29,6 +33,12 @@ static const struct msm_dsi_config msm8974_apq8084_dsi_cfg = { {"vddio", 1800000, 1800000, 100000, 100}, }, }, + .bus_clk_names = dsi_6g_bus_clk_names, + .num_bus_clks = ARRAY_SIZE(dsi_6g_bus_clk_names), +}; + +static const char * const dsi_8916_bus_clk_names[] = { + "mdp_core_clk", "iface_clk", "bus_clk", }; static const struct msm_dsi_config msm8916_dsi_cfg = { @@ -42,6 +52,8 @@ static const struct msm_dsi_config msm8916_dsi_cfg = { {"vddio", 1800000, 1800000, 100000, 100}, }, }, + .bus_clk_names = dsi_8916_bus_clk_names, + .num_bus_clks = ARRAY_SIZE(dsi_8916_bus_clk_names), }; static const struct msm_dsi_config msm8994_dsi_cfg = { @@ -57,7 +69,9 @@ static const struct msm_dsi_config msm8994_dsi_cfg = { {"lab_reg", -1, -1, -1, -1}, {"ibb_reg", -1, -1, -1, -1}, }, - } + }, + .bus_clk_names = dsi_6g_bus_clk_names, + .num_bus_clks = ARRAY_SIZE(dsi_6g_bus_clk_names), }; static const struct msm_dsi_cfg_handler dsi_cfg_handlers[] = { diff --git a/drivers/gpu/drm/msm/dsi/dsi_cfg.h b/drivers/gpu/drm/msm/dsi/dsi_cfg.h index 4cf887240177..5a36836dd6fc 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_cfg.h +++ b/drivers/gpu/drm/msm/dsi/dsi_cfg.h @@ -30,6 +30,8 @@ struct msm_dsi_config { u32 io_offset; struct dsi_reg_config reg_cfg; + const char * const *bus_clk_names; + const int num_bus_clks; }; struct msm_dsi_cfg_handler { diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index f78702472819..56c75354fe03 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -103,10 +103,9 @@ struct msm_dsi_host { void __iomem *ctrl_base; struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX]; - struct clk *mdp_core_clk; - struct clk *ahb_clk; - struct clk *axi_clk; - struct clk *mmss_misc_ahb_clk; + + struct clk *bus_clks[DSI_BUS_CLK_MAX]; + struct clk *byte_clk; struct clk *esc_clk; struct clk *pixel_clk; @@ -319,40 +318,22 @@ static int dsi_regulator_init(struct msm_dsi_host *msm_host) static int dsi_clk_init(struct msm_dsi_host *msm_host) { struct device *dev = &msm_host->pdev->dev; - int ret = 0; - - msm_host->mdp_core_clk = devm_clk_get(dev, "mdp_core_clk"); - if (IS_ERR(msm_host->mdp_core_clk)) { - ret = PTR_ERR(msm_host->mdp_core_clk); - pr_err("%s: Unable to get mdp core clk. ret=%d\n", - __func__, ret); - goto exit; - } - - msm_host->ahb_clk = devm_clk_get(dev, "iface_clk"); - if (IS_ERR(msm_host->ahb_clk)) { - ret = PTR_ERR(msm_host->ahb_clk); - pr_err("%s: Unable to get mdss ahb clk. ret=%d\n", - __func__, ret); - goto exit; - } - - msm_host->axi_clk = devm_clk_get(dev, "bus_clk"); - if (IS_ERR(msm_host->axi_clk)) { - ret = PTR_ERR(msm_host->axi_clk); - pr_err("%s: Unable to get axi bus clk. ret=%d\n", - __func__, ret); - goto exit; - } - - msm_host->mmss_misc_ahb_clk = devm_clk_get(dev, "core_mmss_clk"); - if (IS_ERR(msm_host->mmss_misc_ahb_clk)) { - ret = PTR_ERR(msm_host->mmss_misc_ahb_clk); - pr_err("%s: Unable to get mmss misc ahb clk. ret=%d\n", - __func__, ret); - goto exit; + const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; + int i, ret = 0; + + /* get bus clocks */ + for (i = 0; i < cfg->num_bus_clks; i++) { + msm_host->bus_clks[i] = devm_clk_get(dev, + cfg->bus_clk_names[i]); + if (IS_ERR(msm_host->bus_clks[i])) { + ret = PTR_ERR(msm_host->bus_clks[i]); + pr_err("%s: Unable to get %s, ret = %d\n", + __func__, cfg->bus_clk_names[i], ret); + goto exit; + } } + /* get link and source clocks */ msm_host->byte_clk = devm_clk_get(dev, "byte_clk"); if (IS_ERR(msm_host->byte_clk)) { ret = PTR_ERR(msm_host->byte_clk); @@ -399,55 +380,37 @@ exit: static int dsi_bus_clk_enable(struct msm_dsi_host *msm_host) { - int ret; + const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; + int i, ret; DBG("id=%d", msm_host->id); - ret = clk_prepare_enable(msm_host->mdp_core_clk); - if (ret) { - pr_err("%s: failed to enable mdp_core_clock, %d\n", - __func__, ret); - goto core_clk_err; - } - - ret = clk_prepare_enable(msm_host->ahb_clk); - if (ret) { - pr_err("%s: failed to enable ahb clock, %d\n", __func__, ret); - goto ahb_clk_err; - } - - ret = clk_prepare_enable(msm_host->axi_clk); - if (ret) { - pr_err("%s: failed to enable ahb clock, %d\n", __func__, ret); - goto axi_clk_err; - } - - ret = clk_prepare_enable(msm_host->mmss_misc_ahb_clk); - if (ret) { - pr_err("%s: failed to enable mmss misc ahb clk, %d\n", - __func__, ret); - goto misc_ahb_clk_err; + for (i = 0; i < cfg->num_bus_clks; i++) { + ret = clk_prepare_enable(msm_host->bus_clks[i]); + if (ret) { + pr_err("%s: failed to enable bus clock %d ret %d\n", + __func__, i, ret); + goto err; + } } return 0; +err: + for (; i > 0; i--) + clk_disable_unprepare(msm_host->bus_clks[i]); -misc_ahb_clk_err: - clk_disable_unprepare(msm_host->axi_clk); -axi_clk_err: - clk_disable_unprepare(msm_host->ahb_clk); -ahb_clk_err: - clk_disable_unprepare(msm_host->mdp_core_clk); -core_clk_err: return ret; } static void dsi_bus_clk_disable(struct msm_dsi_host *msm_host) { + const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; + int i; + DBG(""); - clk_disable_unprepare(msm_host->mmss_misc_ahb_clk); - clk_disable_unprepare(msm_host->axi_clk); - clk_disable_unprepare(msm_host->ahb_clk); - clk_disable_unprepare(msm_host->mdp_core_clk); + + for (i = cfg->num_bus_clks - 1; i >= 0; i--) + clk_disable_unprepare(msm_host->bus_clks[i]); } static int dsi_link_clk_enable(struct msm_dsi_host *msm_host) -- cgit v1.2.3 From 4bfa97489ae786c95d0f2999326a524443013cc1 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Fri, 9 Oct 2015 16:32:38 +0530 Subject: drm/msm/dsi: Set up link clocks for DSIv2 DSIv2 (DSI on older A family chips) has slightly different link clock requirements. First, we have an extra clock called src_clk (with a dedicated RCG). This is required by the DSI controller to process the pixel data coming from MDP. It needs to be set at the rate "pclk * bytes_per_pixel". We also need to explicitly configure esc_clk. On DSI6G chips, we don't need to set a rate to esc_clk because its RCG is always sourced from crystal clock (19.2 Mhz in all cases), which is within the escape clock frequency range in the mipi DSI spec. For chips with DSIv2, the crystal clock rate may not be within the required range (27Mhz on APQ8064). Therefore, we derive it from the DSI byte clock. We calculate an esc_clck rate that is within the mipi spec and also divisible by the byte clock rate. When setting rate and enabling the link clocks, we make sure that byte_clk is configured before esc_clk, and src_clk before pixel_clk. We create two different link_enable funcs for DSI6G and DSIv2 since the sequences are different. We also obtain two extra source clocks (dsi_src_clk and esc_src_clk) and set their parent to the clocks provided by DSI PLL. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/dsi/dsi_host.c | 191 ++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 5 deletions(-) (limited to 'drivers/gpu/drm/msm/dsi/dsi_host.c') diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 56c75354fe03..6ec217879c1b 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -113,6 +113,14 @@ struct msm_dsi_host { struct clk *pixel_clk_src; u32 byte_clk_rate; + u32 esc_clk_rate; + + /* DSI v2 specific clocks */ + struct clk *src_clk; + struct clk *esc_clk_src; + struct clk *dsi_clk_src; + + u32 src_clk_rate; struct gpio_desc *disp_en_gpio; struct gpio_desc *te_gpio; @@ -318,7 +326,8 @@ static int dsi_regulator_init(struct msm_dsi_host *msm_host) static int dsi_clk_init(struct msm_dsi_host *msm_host) { struct device *dev = &msm_host->pdev->dev; - const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; + const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; + const struct msm_dsi_config *cfg = cfg_hnd->cfg; int i, ret = 0; /* get bus clocks */ @@ -372,8 +381,34 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host) if (!msm_host->pixel_clk_src) { ret = -ENODEV; pr_err("%s: can't find pixel_clk_src. ret=%d\n", __func__, ret); + goto exit; } + if (cfg_hnd->major == MSM_DSI_VER_MAJOR_V2) { + msm_host->src_clk = devm_clk_get(dev, "src_clk"); + if (IS_ERR(msm_host->src_clk)) { + ret = PTR_ERR(msm_host->src_clk); + pr_err("%s: can't find dsi_src_clk. ret=%d\n", + __func__, ret); + msm_host->src_clk = NULL; + goto exit; + } + + msm_host->esc_clk_src = clk_get_parent(msm_host->esc_clk); + if (!msm_host->esc_clk_src) { + ret = -ENODEV; + pr_err("%s: can't get esc_clk_src. ret=%d\n", + __func__, ret); + goto exit; + } + + msm_host->dsi_clk_src = clk_get_parent(msm_host->src_clk); + if (!msm_host->dsi_clk_src) { + ret = -ENODEV; + pr_err("%s: can't get dsi_clk_src. ret=%d\n", + __func__, ret); + } + } exit: return ret; } @@ -413,7 +448,7 @@ static void dsi_bus_clk_disable(struct msm_dsi_host *msm_host) clk_disable_unprepare(msm_host->bus_clks[i]); } -static int dsi_link_clk_enable(struct msm_dsi_host *msm_host) +static int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host) { int ret; @@ -460,11 +495,98 @@ error: return ret; } -static void dsi_link_clk_disable(struct msm_dsi_host *msm_host) +static int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host) { + int ret; + + DBG("Set clk rates: pclk=%d, byteclk=%d, esc_clk=%d, dsi_src_clk=%d", + msm_host->mode->clock, msm_host->byte_clk_rate, + msm_host->esc_clk_rate, msm_host->src_clk_rate); + + ret = clk_set_rate(msm_host->byte_clk, msm_host->byte_clk_rate); + if (ret) { + pr_err("%s: Failed to set rate byte clk, %d\n", __func__, ret); + goto error; + } + + ret = clk_set_rate(msm_host->esc_clk, msm_host->esc_clk_rate); + if (ret) { + pr_err("%s: Failed to set rate esc clk, %d\n", __func__, ret); + goto error; + } + + ret = clk_set_rate(msm_host->src_clk, msm_host->src_clk_rate); + if (ret) { + pr_err("%s: Failed to set rate src clk, %d\n", __func__, ret); + goto error; + } + + ret = clk_set_rate(msm_host->pixel_clk, msm_host->mode->clock * 1000); + if (ret) { + pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret); + goto error; + } + + ret = clk_prepare_enable(msm_host->byte_clk); + if (ret) { + pr_err("%s: Failed to enable dsi byte clk\n", __func__); + goto error; + } + + ret = clk_prepare_enable(msm_host->esc_clk); + if (ret) { + pr_err("%s: Failed to enable dsi esc clk\n", __func__); + goto esc_clk_err; + } + + ret = clk_prepare_enable(msm_host->src_clk); + if (ret) { + pr_err("%s: Failed to enable dsi src clk\n", __func__); + goto src_clk_err; + } + + ret = clk_prepare_enable(msm_host->pixel_clk); + if (ret) { + pr_err("%s: Failed to enable dsi pixel clk\n", __func__); + goto pixel_clk_err; + } + + return 0; + +pixel_clk_err: + clk_disable_unprepare(msm_host->src_clk); +src_clk_err: clk_disable_unprepare(msm_host->esc_clk); - clk_disable_unprepare(msm_host->pixel_clk); +esc_clk_err: clk_disable_unprepare(msm_host->byte_clk); +error: + return ret; +} + +static int dsi_link_clk_enable(struct msm_dsi_host *msm_host) +{ + const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; + + if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) + return dsi_link_clk_enable_6g(msm_host); + else + return dsi_link_clk_enable_v2(msm_host); +} + +static void dsi_link_clk_disable(struct msm_dsi_host *msm_host) +{ + const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; + + if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) { + clk_disable_unprepare(msm_host->esc_clk); + clk_disable_unprepare(msm_host->pixel_clk); + clk_disable_unprepare(msm_host->byte_clk); + } else { + clk_disable_unprepare(msm_host->pixel_clk); + clk_disable_unprepare(msm_host->src_clk); + clk_disable_unprepare(msm_host->esc_clk); + clk_disable_unprepare(msm_host->byte_clk); + } } static int dsi_clk_ctrl(struct msm_dsi_host *msm_host, bool enable) @@ -499,6 +621,7 @@ unlock_ret: static int dsi_calc_clk_rate(struct msm_dsi_host *msm_host) { struct drm_display_mode *mode = msm_host->mode; + const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; u8 lanes = msm_host->lanes; u32 bpp = dsi_get_bpp(msm_host->format); u32 pclk_rate; @@ -518,6 +641,47 @@ static int dsi_calc_clk_rate(struct msm_dsi_host *msm_host) DBG("pclk=%d, bclk=%d", pclk_rate, msm_host->byte_clk_rate); + msm_host->esc_clk_rate = clk_get_rate(msm_host->esc_clk); + + if (cfg_hnd->major == MSM_DSI_VER_MAJOR_V2) { + unsigned int esc_mhz, esc_div; + unsigned long byte_mhz; + + msm_host->src_clk_rate = (pclk_rate * bpp) / 8; + + /* + * esc clock is byte clock followed by a 4 bit divider, + * we need to find an escape clock frequency within the + * mipi DSI spec range within the maximum divider limit + * We iterate here between an escape clock frequencey + * between 20 Mhz to 5 Mhz and pick up the first one + * that can be supported by our divider + */ + + byte_mhz = msm_host->byte_clk_rate / 1000000; + + for (esc_mhz = 20; esc_mhz >= 5; esc_mhz--) { + esc_div = DIV_ROUND_UP(byte_mhz, esc_mhz); + + /* + * TODO: Ideally, we shouldn't know what sort of divider + * is available in mmss_cc, we're just assuming that + * it'll always be a 4 bit divider. Need to come up with + * a better way here. + */ + if (esc_div >= 1 && esc_div <= 16) + break; + } + + if (esc_mhz < 5) + return -EINVAL; + + msm_host->esc_clk_rate = msm_host->byte_clk_rate / esc_div; + + DBG("esc=%d, src=%d", msm_host->esc_clk_rate, + msm_host->src_clk_rate); + } + return 0; } @@ -1750,6 +1914,7 @@ 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); + const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; struct clk *byte_clk_provider, *pixel_clk_provider; int ret; @@ -1775,6 +1940,22 @@ int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host, goto exit; } + if (cfg_hnd->major == MSM_DSI_VER_MAJOR_V2) { + ret = clk_set_parent(msm_host->dsi_clk_src, pixel_clk_provider); + if (ret) { + pr_err("%s: can't set parent to dsi_clk_src. ret=%d\n", + __func__, ret); + goto exit; + } + + ret = clk_set_parent(msm_host->esc_clk_src, byte_clk_provider); + if (ret) { + pr_err("%s: can't set parent to esc_clk_src. ret=%d\n", + __func__, ret); + goto exit; + } + } + exit: return ret; } @@ -1846,7 +2027,7 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host) dsi_phy_sw_reset(msm_host); ret = msm_dsi_manager_phy_enable(msm_host->id, msm_host->byte_clk_rate * 8, - clk_get_rate(msm_host->esc_clk), + msm_host->esc_clk_rate, &clk_pre, &clk_post); dsi_bus_clk_disable(msm_host); if (ret) { -- cgit v1.2.3 From 4ff9d4cbc15a1ac169eaabf2e3d282b7d2d3799d Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Tue, 13 Oct 2015 12:20:47 +0530 Subject: drm/msm/dsi: Don't use iommu for command TX buffer for DSIv2 We currently use iommu allocated DMA buffers for sending DSI commands. DSIv2 doesn't have a port connected to the MDP iommu. Therefore, it can't use iommu allocated buffers to fetch DSI commands. Use a regular contiguous DMA buffer if we are DSIv2. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/dsi/dsi.h | 4 +- drivers/gpu/drm/msm/dsi/dsi_host.c | 110 +++++++++++++++++++++++----------- drivers/gpu/drm/msm/dsi/dsi_manager.c | 6 +- 3 files changed, 79 insertions(+), 41 deletions(-) (limited to 'drivers/gpu/drm/msm/dsi/dsi_host.c') diff --git a/drivers/gpu/drm/msm/dsi/dsi.h b/drivers/gpu/drm/msm/dsi/dsi.h index 7336b55d05cc..749fbb28ec3d 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.h +++ b/drivers/gpu/drm/msm/dsi/dsi.h @@ -91,7 +91,7 @@ int msm_dsi_manager_phy_enable(int id, u32 *clk_pre, u32 *clk_post); void msm_dsi_manager_phy_disable(int id); int msm_dsi_manager_cmd_xfer(int id, const struct mipi_dsi_msg *msg); -bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 iova, u32 len); +bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 dma_base, u32 len); int msm_dsi_manager_register(struct msm_dsi *msm_dsi); void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi); @@ -145,7 +145,7 @@ int msm_dsi_host_cmd_tx(struct mipi_dsi_host *host, int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host, const struct mipi_dsi_msg *msg); void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, - u32 iova, u32 len); + u32 dma_base, u32 len); int msm_dsi_host_enable(struct mipi_dsi_host *host); int msm_dsi_host_disable(struct mipi_dsi_host *host); int msm_dsi_host_power_on(struct mipi_dsi_host *host); diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 6ec217879c1b..bbf7f72058d7 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -138,7 +138,15 @@ struct msm_dsi_host { struct work_struct err_work; struct workqueue_struct *workqueue; + /* DSI 6G TX buffer*/ struct drm_gem_object *tx_gem_obj; + + /* DSI v2 TX buffer */ + void *tx_buf; + dma_addr_t tx_buf_paddr; + + int tx_size; + u8 *rx_buf; struct drm_display_mode *mode; @@ -983,29 +991,46 @@ static void dsi_wait4video_eng_busy(struct msm_dsi_host *msm_host) static int dsi_tx_buf_alloc(struct msm_dsi_host *msm_host, int size) { struct drm_device *dev = msm_host->dev; + const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; int ret; u32 iova; - mutex_lock(&dev->struct_mutex); - msm_host->tx_gem_obj = msm_gem_new(dev, size, MSM_BO_UNCACHED); - if (IS_ERR(msm_host->tx_gem_obj)) { - ret = PTR_ERR(msm_host->tx_gem_obj); - pr_err("%s: failed to allocate gem, %d\n", __func__, ret); - msm_host->tx_gem_obj = NULL; + if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) { + mutex_lock(&dev->struct_mutex); + msm_host->tx_gem_obj = msm_gem_new(dev, size, MSM_BO_UNCACHED); + if (IS_ERR(msm_host->tx_gem_obj)) { + ret = PTR_ERR(msm_host->tx_gem_obj); + pr_err("%s: failed to allocate gem, %d\n", + __func__, ret); + msm_host->tx_gem_obj = NULL; + mutex_unlock(&dev->struct_mutex); + return ret; + } + + ret = msm_gem_get_iova_locked(msm_host->tx_gem_obj, 0, &iova); + if (ret) { + pr_err("%s: failed to get iova, %d\n", __func__, ret); + return ret; + } mutex_unlock(&dev->struct_mutex); - return ret; - } - ret = msm_gem_get_iova_locked(msm_host->tx_gem_obj, 0, &iova); - if (ret) { - pr_err("%s: failed to get iova, %d\n", __func__, ret); - return ret; - } - mutex_unlock(&dev->struct_mutex); + if (iova & 0x07) { + pr_err("%s: buf NOT 8 bytes aligned\n", __func__); + return -EINVAL; + } - if (iova & 0x07) { - pr_err("%s: buf NOT 8 bytes aligned\n", __func__); - return -EINVAL; + msm_host->tx_size = msm_host->tx_gem_obj->size; + } else { + msm_host->tx_buf = dma_alloc_coherent(dev->dev, size, + &msm_host->tx_buf_paddr, GFP_KERNEL); + if (!msm_host->tx_buf) { + ret = -ENOMEM; + pr_err("%s: failed to allocate tx buf, %d\n", + __func__, ret); + return ret; + } + + msm_host->tx_size = size; } return 0; @@ -1022,14 +1047,19 @@ static void dsi_tx_buf_free(struct msm_dsi_host *msm_host) msm_host->tx_gem_obj = NULL; mutex_unlock(&dev->struct_mutex); } + + if (msm_host->tx_buf) + dma_free_coherent(dev->dev, msm_host->tx_size, msm_host->tx_buf, + msm_host->tx_buf_paddr); } /* * prepare cmd buffer to be txed */ -static int dsi_cmd_dma_add(struct drm_gem_object *tx_gem, - const struct mipi_dsi_msg *msg) +static int dsi_cmd_dma_add(struct msm_dsi_host *msm_host, + const struct mipi_dsi_msg *msg) { + const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; struct mipi_dsi_packet packet; int len; int ret; @@ -1042,17 +1072,20 @@ static int dsi_cmd_dma_add(struct drm_gem_object *tx_gem, } len = (packet.size + 3) & (~0x3); - if (len > tx_gem->size) { + if (len > msm_host->tx_size) { pr_err("%s: packet size is too big\n", __func__); return -EINVAL; } - data = msm_gem_vaddr(tx_gem); - - if (IS_ERR(data)) { - ret = PTR_ERR(data); - pr_err("%s: get vaddr failed, %d\n", __func__, ret); - return ret; + if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) { + data = msm_gem_vaddr(msm_host->tx_gem_obj); + if (IS_ERR(data)) { + ret = PTR_ERR(data); + pr_err("%s: get vaddr failed, %d\n", __func__, ret); + return ret; + } + } else { + data = msm_host->tx_buf; } /* MSM specific command format in memory */ @@ -1118,17 +1151,21 @@ static int dsi_long_read_resp(u8 *buf, const struct mipi_dsi_msg *msg) return msg->rx_len; } - static int dsi_cmd_dma_tx(struct msm_dsi_host *msm_host, int len) { + const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; int ret; - u32 iova; + u32 dma_base; bool triggered; - ret = msm_gem_get_iova(msm_host->tx_gem_obj, 0, &iova); - if (ret) { - pr_err("%s: failed to get iova: %d\n", __func__, ret); - return ret; + if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) { + ret = msm_gem_get_iova(msm_host->tx_gem_obj, 0, &dma_base); + if (ret) { + pr_err("%s: failed to get iova: %d\n", __func__, ret); + return ret; + } + } else { + dma_base = msm_host->tx_buf_paddr; } reinit_completion(&msm_host->dma_comp); @@ -1136,7 +1173,7 @@ static int dsi_cmd_dma_tx(struct msm_dsi_host *msm_host, int len) dsi_wait4video_eng_busy(msm_host); triggered = msm_dsi_manager_cmd_xfer_trigger( - msm_host->id, iova, len); + msm_host->id, dma_base, len); if (triggered) { ret = wait_for_completion_timeout(&msm_host->dma_comp, msecs_to_jiffies(200)); @@ -1208,7 +1245,7 @@ static int dsi_cmds2buf_tx(struct msm_dsi_host *msm_host, int bllp_len = msm_host->mode->hdisplay * dsi_get_bpp(msm_host->format) / 8; - len = dsi_cmd_dma_add(msm_host->tx_gem_obj, msg); + len = dsi_cmd_dma_add(msm_host, msg); if (!len) { pr_err("%s: failed to add cmd type = 0x%x\n", __func__, msg->type); @@ -1898,11 +1935,12 @@ int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host, return ret; } -void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 iova, u32 len) +void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 dma_base, + u32 len) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); - dsi_write(msm_host, REG_DSI_DMA_BASE, iova); + dsi_write(msm_host, REG_DSI_DMA_BASE, dma_base); dsi_write(msm_host, REG_DSI_DMA_LEN, len); dsi_write(msm_host, REG_DSI_TRIG_DMA, 1); diff --git a/drivers/gpu/drm/msm/dsi/dsi_manager.c b/drivers/gpu/drm/msm/dsi/dsi_manager.c index 0455ff75074a..58ba7ec17f51 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -774,7 +774,7 @@ restore_host0: return ret; } -bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 iova, u32 len) +bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 dma_base, u32 len) { struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); struct msm_dsi *msm_dsi0 = dsi_mgr_get_dsi(DSI_0); @@ -784,9 +784,9 @@ bool msm_dsi_manager_cmd_xfer_trigger(int id, u32 iova, u32 len) return false; if (IS_SYNC_NEEDED() && msm_dsi0) - msm_dsi_host_cmd_xfer_commit(msm_dsi0->host, iova, len); + msm_dsi_host_cmd_xfer_commit(msm_dsi0->host, dma_base, len); - msm_dsi_host_cmd_xfer_commit(host, iova, len); + msm_dsi_host_cmd_xfer_commit(host, dma_base, len); return true; } -- cgit v1.2.3 From 0c7df47f6a4c5d7c6c4fa1f5ecbee64c441f5939 Mon Sep 17 00:00:00 2001 From: Archit Taneja Date: Wed, 14 Oct 2015 15:31:13 +0530 Subject: drm/msm/dsi: Enable MMSS SPFB port via syscon For DSIv2 to work, we need to enable MMSS_AHB_ARB_MASTER_PORT in MMSS_SFPB. We enable the required bitfield by retrieving MMSS_SFPB regmap pointer via syscon. Signed-off-by: Archit Taneja Signed-off-by: Rob Clark --- drivers/gpu/drm/msm/dsi/dsi_host.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'drivers/gpu/drm/msm/dsi/dsi_host.c') diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index bbf7f72058d7..f61b88e91a8b 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -24,10 +24,13 @@ #include #include #include +#include +#include #include