diff options
Diffstat (limited to 'drivers/gpu/drm/msm/dsi/dsi_host.c')
| -rw-r--r-- | drivers/gpu/drm/msm/dsi/dsi_host.c | 1385 |
1 files changed, 785 insertions, 600 deletions
diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 38e481d2d606..e0de545d4077 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -1,38 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 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 <linux/clk.h> #include <linux/delay.h> +#include <linux/dma-mapping.h> #include <linux/err.h> -#include <linux/gpio.h> -#include <linux/gpio/consumer.h> #include <linux/interrupt.h> -#include <linux/of_device.h> -#include <linux/of_gpio.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_graph.h> #include <linux/of_irq.h> #include <linux/pinctrl/consumer.h> -#include <linux/of_graph.h> +#include <linux/pm_opp.h> +#include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/spinlock.h> -#include <linux/mfd/syscon.h> -#include <linux/regmap.h> + #include <video/mipi_display.h> +#include <drm/display/drm_dsc_helper.h> +#include <drm/drm_of.h> + #include "dsi.h" #include "dsi.xml.h" #include "sfpb.xml.h" #include "dsi_cfg.h" +#include "msm_dsc_helper.h" #include "msm_kms.h" +#include "msm_gem.h" +#include "phy/dsi_phy.h" + +#define DSI_RESET_TOGGLE_DELAY_MS 20 + +static int dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc_config *dsc); static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor) { @@ -52,7 +54,7 @@ static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor) * scratch register which we never touch) */ - ver = msm_readl(base + REG_DSI_VERSION); + ver = readl(base + REG_DSI_VERSION); if (ver) { /* older dsi host, there is no register shift */ ver = FIELD(ver, DSI_VERSION_MAJOR); @@ -70,12 +72,12 @@ static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor) * registers are shifted down, read DSI_VERSION again with * the shifted offset */ - ver = msm_readl(base + DSI_6G_REG_SHIFT + REG_DSI_VERSION); + ver = 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 = msm_readl(base + REG_DSI_6G_HW_VERSION); + *minor = readl(base + REG_DSI_6G_HW_VERSION); return 0; } else { return -EINVAL; @@ -106,30 +108,35 @@ struct msm_dsi_host { int id; void __iomem *ctrl_base; - struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX]; + phys_addr_t ctrl_size; + struct regulator_bulk_data *supplies; - struct clk *bus_clks[DSI_BUS_CLK_MAX]; + int num_bus_clks; + struct clk_bulk_data bus_clks[DSI_BUS_CLK_MAX]; struct clk *byte_clk; struct clk *esc_clk; struct clk *pixel_clk; - struct clk *byte_clk_src; - struct clk *pixel_clk_src; struct clk *byte_intf_clk; - u32 byte_clk_rate; - u32 pixel_clk_rate; - u32 esc_clk_rate; + /* + * Clocks which needs to be properly parented between DISPCC and DSI PHY + * PLL: + */ + struct clk *byte_src_clk; + struct clk *pixel_src_clk; + struct clk *dsi_pll_byte_clk; + struct clk *dsi_pll_pixel_clk; + + unsigned long byte_clk_rate; + unsigned long byte_intf_clk_rate; + unsigned long pixel_clk_rate; + unsigned long 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; + unsigned long src_clk_rate; const struct msm_dsi_cfg_handler *cfg_hnd; @@ -141,11 +148,11 @@ struct msm_dsi_host { u32 err_work_state; struct work_struct err_work; - struct work_struct hpd_work; struct workqueue_struct *workqueue; /* DSI 6G TX buffer*/ struct drm_gem_object *tx_gem_obj; + struct drm_gpuvm *vm; /* DSI v2 TX buffer */ void *tx_buf; @@ -158,9 +165,9 @@ struct msm_dsi_host { struct regmap *sfpb; struct drm_display_mode *mode; + struct drm_dsc_config *dsc; /* connected device info */ - struct device_node *device_node; unsigned int channel; unsigned int lanes; enum mipi_dsi_pixel_format format; @@ -170,6 +177,9 @@ struct msm_dsi_host { int dlane_swap; int num_data_lanes; + /* from phy DT */ + bool cphy_mode; + u32 dma_cmd_ctrl_restore; bool registered; @@ -178,68 +188,43 @@ struct msm_dsi_host { int irq; }; -static u32 dsi_get_bpp(const enum mipi_dsi_pixel_format fmt) -{ - switch (fmt) { - case MIPI_DSI_FMT_RGB565: return 16; - case MIPI_DSI_FMT_RGB666_PACKED: return 18; - case MIPI_DSI_FMT_RGB666: - case MIPI_DSI_FMT_RGB888: - default: return 24; - } -} - static inline u32 dsi_read(struct msm_dsi_host *msm_host, u32 reg) { - return msm_readl(msm_host->ctrl_base + reg); + return readl(msm_host->ctrl_base + reg); } + static inline void dsi_write(struct msm_dsi_host *msm_host, u32 reg, u32 data) { - msm_writel(data, msm_host->ctrl_base + reg); + writel(data, msm_host->ctrl_base + reg); } -static int dsi_host_regulator_enable(struct msm_dsi_host *msm_host); -static void dsi_host_regulator_disable(struct msm_dsi_host *msm_host); - -static const struct msm_dsi_cfg_handler *dsi_get_config( - struct msm_dsi_host *msm_host) +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(dev, "gdsc"); - if (IS_ERR(gdsc_reg)) { - pr_err("%s: cannot get gdsc\n", __func__); - goto exit; - } - ahb_clk = msm_clk_get(msm_host->pdev, "iface"); if (IS_ERR(ahb_clk)) { - pr_err("%s: cannot get interface clock\n", __func__); - goto put_gdsc; + dev_err_probe(dev, PTR_ERR(ahb_clk), "%s: cannot get interface clock\n", + __func__); + goto exit; } pm_runtime_get_sync(dev); - ret = regulator_enable(gdsc_reg); - if (ret) { - pr_err("%s: unable to enable gdsc\n", __func__); - goto put_gdsc; - } - ret = clk_prepare_enable(ahb_clk); if (ret) { - pr_err("%s: unable to enable ahb_clk\n", __func__); - goto disable_gdsc; + dev_err_probe(dev, ret, "%s: unable to enable ahb_clk\n", __func__); + goto runtime_put; } ret = dsi_get_version(msm_host->ctrl_base, &major, &minor); if (ret) { - pr_err("%s: Invalid version\n", __func__); + dev_err_probe(dev, ret, "%s: Invalid version\n", __func__); goto disable_clks; } @@ -249,11 +234,8 @@ static const struct msm_dsi_cfg_handler *dsi_get_config( disable_clks: clk_disable_unprepare(ahb_clk); -disable_gdsc: - regulator_disable(gdsc_reg); +runtime_put: pm_runtime_put_sync(dev); -put_gdsc: - regulator_put(gdsc_reg); exit: return cfg_hnd; } @@ -263,76 +245,6 @@ static inline struct msm_dsi_host *to_msm_dsi_host(struct mipi_dsi_host *host) return container_of(host, struct msm_dsi_host, base); } -static void dsi_host_regulator_disable(struct msm_dsi_host *msm_host) -{ - struct regulator_bulk_data *s = msm_host->supplies; - const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; - int num = msm_host->cfg_hnd->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_host_regulator_enable(struct msm_dsi_host *msm_host) -{ - struct regulator_bulk_data *s = msm_host->supplies; - const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; - int num = msm_host->cfg_hnd->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) { - pr_err("regulator %d set op mode failed, %d\n", - i, ret); - goto fail; - } - } - } - - ret = regulator_bulk_enable(num, s); - if (ret < 0) { - pr_err("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; -} - -static int dsi_regulator_init(struct msm_dsi_host *msm_host) -{ - struct regulator_bulk_data *s = msm_host->supplies; - const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; - int num = msm_host->cfg_hnd->cfg->reg_cfg.num; - int i, ret; - - for (i = 0; i < num; i++) - s[i].supply = regs[i].name; - - ret = devm_regulator_bulk_get(&msm_host->pdev->dev, num, s); - if (ret < 0) { - pr_err("%s: failed to init regulator, ret=%d\n", - __func__, ret); - return ret; - } - - return 0; -} - int dsi_clk_init_v2(struct msm_dsi_host *msm_host) { struct platform_device *pdev = msm_host->pdev; @@ -348,21 +260,6 @@ int dsi_clk_init_v2(struct msm_dsi_host *msm_host) return ret; } - 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 clock parent. ret=%d\n", - __func__, ret); - return ret; - } - - 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 src clock parent. ret=%d\n", - __func__, ret); - } - return ret; } @@ -381,6 +278,38 @@ int dsi_clk_init_6g_v2(struct msm_dsi_host *msm_host) return ret; } +int dsi_clk_init_6g_v2_9(struct msm_dsi_host *msm_host) +{ + struct device *dev = &msm_host->pdev->dev; + int ret; + + ret = dsi_clk_init_6g_v2(msm_host); + if (ret) + return ret; + + msm_host->byte_src_clk = devm_clk_get(dev, "byte_src"); + if (IS_ERR(msm_host->byte_src_clk)) + return dev_err_probe(dev, PTR_ERR(msm_host->byte_src_clk), + "can't get byte_src clock\n"); + + msm_host->dsi_pll_byte_clk = devm_clk_get(dev, "dsi_pll_byte"); + if (IS_ERR(msm_host->dsi_pll_byte_clk)) + return dev_err_probe(dev, PTR_ERR(msm_host->dsi_pll_byte_clk), + "can't get dsi_pll_byte clock\n"); + + msm_host->pixel_src_clk = devm_clk_get(dev, "pixel_src"); + if (IS_ERR(msm_host->pixel_src_clk)) + return dev_err_probe(dev, PTR_ERR(msm_host->pixel_src_clk), + "can't get pixel_src clock\n"); + + msm_host->dsi_pll_pixel_clk = devm_clk_get(dev, "dsi_pll_pixel"); + if (IS_ERR(msm_host->dsi_pll_pixel_clk)) + return dev_err_probe(dev, PTR_ERR(msm_host->dsi_pll_pixel_clk), + "can't get dsi_pll_pixel clock\n"); + + return 0; +} + static int dsi_clk_init(struct msm_dsi_host *msm_host) { struct platform_device *pdev = msm_host->pdev; @@ -389,100 +318,39 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host) int i, ret = 0; /* get bus clocks */ - for (i = 0; i < cfg->num_bus_clks; i++) { - msm_host->bus_clks[i] = msm_clk_get(pdev, - 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 clock, ret = %d\n", - __func__, cfg->bus_clk_names[i], ret); - goto exit; - } - } + for (i = 0; i < cfg->num_bus_clks; i++) + msm_host->bus_clks[i].id = cfg->bus_clk_names[i]; + msm_host->num_bus_clks = cfg->num_bus_clks; + + ret = devm_clk_bulk_get(&pdev->dev, msm_host->num_bus_clks, msm_host->bus_clks); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Unable to get clocks\n"); /* get link and source clocks */ msm_host->byte_clk = msm_clk_get(pdev, "byte"); - if (IS_ERR(msm_host->byte_clk)) { - ret = PTR_ERR(msm_host->byte_clk); - pr_err("%s: can't find dsi_byte clock. ret=%d\n", - __func__, ret); - msm_host->byte_clk = NULL; - goto exit; - } + if (IS_ERR(msm_host->byte_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(msm_host->byte_clk), + "%s: can't find dsi_byte clock\n", + __func__); msm_host->pixel_clk = msm_clk_get(pdev, "pixel"); - if (IS_ERR(msm_host->pixel_clk)) { - ret = PTR_ERR(msm_host->pixel_clk); - pr_err("%s: can't find dsi_pixel clock. ret=%d\n", - __func__, ret); - msm_host->pixel_clk = NULL; - goto exit; - } + if (IS_ERR(msm_host->pixel_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(msm_host->pixel_clk), + "%s: can't find dsi_pixel clock\n", + __func__); msm_host->esc_clk = msm_clk_get(pdev, "core"); - if (IS_ERR(msm_host->esc_clk)) { - ret = PTR_ERR(msm_host->esc_clk); - pr_err("%s: can't find dsi_esc clock. ret=%d\n", - __func__, ret); - msm_host->esc_clk = NULL; - goto exit; - } - - 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 clock. ret=%d\n", __func__, ret); - goto exit; - } - - 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 clock. ret=%d\n", __func__, ret); - goto exit; - } + if (IS_ERR(msm_host->esc_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(msm_host->esc_clk), + "%s: can't find dsi_esc clock\n", + __func__); if (cfg_hnd->ops->clk_init_ver) ret = cfg_hnd->ops->clk_init_ver(msm_host); -exit: - return ret; -} - -static int dsi_bus_clk_enable(struct msm_dsi_host *msm_host) -{ - const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; - int i, ret; - - DBG("id=%d", msm_host->id); - - 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]); 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(""); - - for (i = cfg->num_bus_clks - 1; i >= 0; i--) - clk_disable_unprepare(msm_host->bus_clks[i]); -} - int msm_dsi_runtime_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -493,7 +361,7 @@ int msm_dsi_runtime_suspend(struct device *dev) if (!msm_host->cfg_hnd) return 0; - dsi_bus_clk_disable(msm_host); + clk_bulk_disable_unprepare(msm_host->num_bus_clks, msm_host->bus_clks); return 0; } @@ -508,38 +376,65 @@ int msm_dsi_runtime_resume(struct device *dev) if (!msm_host->cfg_hnd) return 0; - return dsi_bus_clk_enable(msm_host); + return clk_bulk_prepare_enable(msm_host->num_bus_clks, msm_host->bus_clks); } -int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host) +int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host) { int ret; - DBG("Set clk rates: pclk=%d, byteclk=%d", - msm_host->mode->clock, msm_host->byte_clk_rate); + DBG("Set clk rates: pclk=%lu, byteclk=%lu", + msm_host->pixel_clk_rate, msm_host->byte_clk_rate); - ret = clk_set_rate(msm_host->byte_clk, msm_host->byte_clk_rate); + ret = dev_pm_opp_set_rate(&msm_host->pdev->dev, + msm_host->byte_clk_rate); if (ret) { - pr_err("%s: Failed to set rate byte clk, %d\n", __func__, ret); - goto error; + pr_err("%s: dev_pm_opp_set_rate failed %d\n", __func__, ret); + return ret; } ret = clk_set_rate(msm_host->pixel_clk, msm_host->pixel_clk_rate); if (ret) { pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret); - goto error; + return ret; } if (msm_host->byte_intf_clk) { - ret = clk_set_rate(msm_host->byte_intf_clk, - msm_host->byte_clk_rate / 2); + ret = clk_set_rate(msm_host->byte_intf_clk, msm_host->byte_intf_clk_rate); if (ret) { pr_err("%s: Failed to set rate byte intf clk, %d\n", __func__, ret); - goto error; + return ret; } } + return 0; +} + +int dsi_link_clk_set_rate_6g_v2_9(struct msm_dsi_host *msm_host) +{ + struct device *dev = &msm_host->pdev->dev; + int ret; + + /* + * DSI PHY PLLs have to be enabled to allow reparenting to them, so + * cannot use assigned-clock-parents. + */ + ret = clk_set_parent(msm_host->byte_src_clk, msm_host->dsi_pll_byte_clk); + if (ret) + dev_err(dev, "Failed to parent byte_src -> dsi_pll_byte: %d\n", ret); + + ret = clk_set_parent(msm_host->pixel_src_clk, msm_host->dsi_pll_pixel_clk); + if (ret) + dev_err(dev, "Failed to parent pixel_src -> dsi_pll_pixel: %d\n", ret); + + return dsi_link_clk_set_rate_6g(msm_host); +} + +int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host) +{ + int ret; + ret = clk_prepare_enable(msm_host->esc_clk); if (ret) { pr_err("%s: Failed to enable dsi esc clk\n", __func__); @@ -558,13 +453,11 @@ int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host) goto pixel_clk_err; } - if (msm_host->byte_intf_clk) { - ret = clk_prepare_enable(msm_host->byte_intf_clk); - if (ret) { - pr_err("%s: Failed to enable byte intf clk\n", - __func__); - goto byte_intf_clk_err; - } + ret = clk_prepare_enable(msm_host->byte_intf_clk); + if (ret) { + pr_err("%s: Failed to enable byte intf clk\n", + __func__); + goto byte_intf_clk_err; } return 0; @@ -579,38 +472,45 @@ error: return ret; } -int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host) +int dsi_link_clk_set_rate_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); + DBG("Set clk rates: pclk=%lu, byteclk=%lu, esc_clk=%lu, dsi_src_clk=%lu", + msm_host->pixel_clk_rate, 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; + return ret; } 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; + return ret; } 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; + return ret; } ret = clk_set_rate(msm_host->pixel_clk, msm_host->pixel_clk_rate); if (ret) { pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret); - goto error; + return ret; } + return 0; +} + +int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host) +{ + int ret; + ret = clk_prepare_enable(msm_host->byte_clk); if (ret) { pr_err("%s: Failed to enable dsi byte clk\n", __func__); @@ -649,10 +549,11 @@ error: void dsi_link_clk_disable_6g(struct msm_dsi_host *msm_host) { + /* Drop the performance state vote */ + dev_pm_opp_set_rate(&msm_host->pdev->dev, 0); clk_disable_unprepare(msm_host->esc_clk); clk_disable_unprepare(msm_host->pixel_clk); - if (msm_host->byte_intf_clk) - clk_disable_unprepare(msm_host->byte_intf_clk); + clk_disable_unprepare(msm_host->byte_intf_clk); clk_disable_unprepare(msm_host->byte_clk); } @@ -664,71 +565,112 @@ void dsi_link_clk_disable_v2(struct msm_dsi_host *msm_host) clk_disable_unprepare(msm_host->byte_clk); } -static u32 dsi_get_pclk_rate(struct msm_dsi_host *msm_host, bool is_dual_dsi) +/** + * dsi_adjust_pclk_for_compression() - Adjust the pclk rate for compression case + * @mode: The selected mode for the DSI output + * @dsc: DRM DSC configuration for this DSI output + * + * Adjust the pclk rate by calculating a new hdisplay proportional to + * the compression ratio such that: + * new_hdisplay = old_hdisplay * compressed_bpp / uncompressed_bpp + * + * Porches do not need to be adjusted: + * - For VIDEO mode they are not compressed by DSC and are passed as is. + * - For CMD mode there are no actual porches. Instead these fields + * currently represent the overhead to the image data transfer. As such, they + * are calculated for the final mode parameters (after the compression) and + * are not to be adjusted too. + * + * FIXME: Reconsider this if/when CMD mode handling is rewritten to use + * transfer time and data overhead as a starting point of the calculations. + */ +static unsigned long dsi_adjust_pclk_for_compression(const struct drm_display_mode *mode, + const struct drm_dsc_config *dsc) { - struct drm_display_mode *mode = msm_host->mode; - u32 pclk_rate; + int new_hdisplay = DIV_ROUND_UP(mode->hdisplay * drm_dsc_get_bpp_int(dsc), + dsc->bits_per_component * 3); - pclk_rate = mode->clock * 1000; + int new_htotal = mode->htotal - mode->hdisplay + new_hdisplay; + + return mult_frac(mode->clock * 1000u, new_htotal, mode->htotal); +} + +static unsigned long dsi_get_pclk_rate(const struct drm_display_mode *mode, + const struct drm_dsc_config *dsc, bool is_bonded_dsi) +{ + unsigned long pclk_rate; + + pclk_rate = mode->clock * 1000u; + + if (dsc) + pclk_rate = dsi_adjust_pclk_for_compression(mode, dsc); /* - * For dual DSI mode, the current DRM mode has the complete width of the + * For bonded DSI mode, the current DRM mode has the complete width of the * panel. Since, the complete panel is driven by two DSI controllers, * the clock rates have to be split between the two dsi controllers. * Adjust the byte and pixel clock rates for each dsi host accordingly. */ - if (is_dual_dsi) + if (is_bonded_dsi) pclk_rate /= 2; return pclk_rate; } -static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_dual_dsi) +unsigned long dsi_byte_clk_get_rate(struct mipi_dsi_host *host, bool is_bonded_dsi, + const struct drm_display_mode *mode) { + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); u8 lanes = msm_host->lanes; - u32 bpp = dsi_get_bpp(msm_host->format); - u32 pclk_rate = dsi_get_pclk_rate(msm_host, is_dual_dsi); - u64 pclk_bpp = (u64)pclk_rate * bpp; + u32 bpp = mipi_dsi_pixel_format_to_bpp(msm_host->format); + unsigned long pclk_rate = dsi_get_pclk_rate(mode, msm_host->dsc, is_bonded_dsi); + unsigned long pclk_bpp; if (lanes == 0) { pr_err("%s: forcing mdss_dsi lanes to 1\n", __func__); lanes = 1; } - do_div(pclk_bpp, (8 * lanes)); + /* CPHY "byte_clk" is in units of 16 bits */ + if (msm_host->cphy_mode) + pclk_bpp = mult_frac(pclk_rate, bpp, 16 * lanes); + else + pclk_bpp = mult_frac(pclk_rate, bpp, 8 * lanes); - msm_host->pixel_clk_rate = pclk_rate; - msm_host->byte_clk_rate = pclk_bpp; + return pclk_bpp; +} - DBG("pclk=%d, bclk=%d", msm_host->pixel_clk_rate, - msm_host->byte_clk_rate); +static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi) +{ + msm_host->pixel_clk_rate = dsi_get_pclk_rate(msm_host->mode, msm_host->dsc, is_bonded_dsi); + msm_host->byte_clk_rate = dsi_byte_clk_get_rate(&msm_host->base, is_bonded_dsi, + msm_host->mode); + DBG("pclk=%lu, bclk=%lu", msm_host->pixel_clk_rate, + msm_host->byte_clk_rate); } -int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_dual_dsi) +int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi) { if (!msm_host->mode) { pr_err("%s: mode not set\n", __func__); return -EINVAL; } - dsi_calc_pclk(msm_host, is_dual_dsi); + dsi_calc_pclk(msm_host, is_bonded_dsi); msm_host->esc_clk_rate = clk_get_rate(msm_host->esc_clk); return 0; } -int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_dual_dsi) +int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_bonded_dsi) { - u32 bpp = dsi_get_bpp(msm_host->format); - u64 pclk_bpp; + u32 bpp = mipi_dsi_pixel_format_to_bpp(msm_host->format); unsigned int esc_mhz, esc_div; unsigned long byte_mhz; - dsi_calc_pclk(msm_host, is_dual_dsi); + dsi_calc_pclk(msm_host, is_bonded_dsi); - pclk_bpp = (u64)dsi_get_pclk_rate(msm_host, is_dual_dsi) * bpp; - do_div(pclk_bpp, 8); - msm_host->src_clk_rate = pclk_bpp; + msm_host->src_clk_rate = mult_frac(msm_host->pixel_clk_rate, bpp, 8); /* * esc clock is byte clock followed by a 4 bit divider, @@ -759,7 +701,7 @@ int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_dual_dsi) msm_host->esc_clk_rate = msm_host->byte_clk_rate / esc_div; - DBG("esc=%d, src=%d", msm_host->esc_clk_rate, + DBG("esc=%lu, src=%lu", msm_host->esc_clk_rate, msm_host->src_clk_rate); return 0; @@ -794,8 +736,8 @@ static inline enum dsi_traffic_mode dsi_get_traffic_mode(const u32 mode_flags) return NON_BURST_SYNCH_EVENT; } -static inline enum dsi_vid_dst_format dsi_get_vid_fmt( - const enum mipi_dsi_pixel_format mipi_fmt) +static inline enum dsi_vid_dst_format +dsi_get_vid_fmt(const enum mipi_dsi_pixel_format mipi_fmt) { switch (mipi_fmt) { case MIPI_DSI_FMT_RGB888: return VID_DST_FORMAT_RGB888; @@ -806,8 +748,8 @@ static inline enum dsi_vid_dst_format dsi_get_vid_fmt( } } -static inline enum dsi_cmd_dst_format dsi_get_cmd_fmt( - const enum mipi_dsi_pixel_format mipi_fmt) +static inline enum dsi_cmd_dst_format +dsi_get_cmd_fmt(const enum mipi_dsi_pixel_format mipi_fmt) { switch (mipi_fmt) { case MIPI_DSI_FMT_RGB888: return CMD_DST_FORMAT_RGB888; @@ -818,27 +760,36 @@ static inline enum dsi_cmd_dst_format dsi_get_cmd_fmt( } } -static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, - struct msm_dsi_phy_shared_timings *phy_shared_timings) +static void dsi_ctrl_disable(struct msm_dsi_host *msm_host) +{ + dsi_write(msm_host, REG_DSI_CTRL, 0); +} + +bool msm_dsi_host_is_wide_bus_enabled(struct mipi_dsi_host *host) +{ + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + + return msm_host->dsc && + (msm_host->cfg_hnd->major == MSM_DSI_VER_MAJOR_6G && + msm_host->cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V2_5_0); +} + +static void dsi_ctrl_enable(struct msm_dsi_host *msm_host, + struct msm_dsi_phy_shared_timings *phy_shared_timings, struct msm_dsi_phy *phy) { u32 flags = msm_host->mode_flags; enum mipi_dsi_pixel_format mipi_fmt = msm_host->format; const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; - u32 data = 0; - - if (!enable) { - dsi_write(msm_host, REG_DSI_CTRL, 0); - return; - } + u32 data = 0, lane_ctrl = 0; if (flags & MIPI_DSI_MODE_VIDEO) { if (flags & MIPI_DSI_MODE_VIDEO_HSE) data |= DSI_VID_CFG0_PULSE_MODE_HSA_HE; - if (flags & MIPI_DSI_MODE_VIDEO_HFP) + if (flags & MIPI_DSI_MODE_VIDEO_NO_HFP) data |= DSI_VID_CFG0_HFP_POWER_STOP; - if (flags & MIPI_DSI_MODE_VIDEO_HBP) + if (flags & MIPI_DSI_MODE_VIDEO_NO_HBP) data |= DSI_VID_CFG0_HBP_POWER_STOP; - if (flags & MIPI_DSI_MODE_VIDEO_HSA) + if (flags & MIPI_DSI_MODE_VIDEO_NO_HSA) data |= DSI_VID_CFG0_HSA_POWER_STOP; /* Always set low power stop mode for BLLP * to let command engine send packets @@ -848,6 +799,8 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, data |= DSI_VID_CFG0_TRAFFIC_MODE(dsi_get_traffic_mode(flags)); data |= DSI_VID_CFG0_DST_FORMAT(dsi_get_vid_fmt(mipi_fmt)); data |= DSI_VID_CFG0_VIRT_CHANNEL(msm_host->channel); + if (msm_dsi_host_is_wide_bus_enabled(&msm_host->base)) + data |= DSI_VID_CFG0_DATABUS_WIDEN; dsi_write(msm_host, REG_DSI_VID_CFG0, data); /* Do not swap RGB colors */ @@ -865,6 +818,18 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, /* Always insert DCS command */ data |= DSI_CMD_CFG1_INSERT_DCS_COMMAND; dsi_write(msm_host, REG_DSI_CMD_CFG1, data); + + if (cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) { + data = dsi_read(msm_host, REG_DSI_CMD_MODE_MDP_CTRL2); + + if (cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_3) + data |= DSI_CMD_MODE_MDP_CTRL2_BURST_MODE; + + if (msm_dsi_host_is_wide_bus_enabled(&msm_host->base)) + data |= DSI_CMD_MODE_MDP_CTRL2_DATABUS_WIDEN; + + dsi_write(msm_host, REG_DSI_CMD_MODE_MDP_CTRL2, data); + } } dsi_write(msm_host, REG_DSI_CMD_DMA_CTRL, @@ -893,7 +858,7 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, DSI_T_CLK_PRE_EXTEND_INC_BY_2_BYTECLK); data = 0; - if (!(flags & MIPI_DSI_MODE_EOT_PACKET)) + if (!(flags & MIPI_DSI_MODE_NO_EOT_PACKET)) data |= DSI_EOT_PACKET_CTRL_TX_EOT_APPEND; dsi_write(msm_host, REG_DSI_EOT_PACKET_CTRL, data); @@ -912,16 +877,87 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, dsi_write(msm_host, REG_DSI_LANE_SWAP_CTRL, DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(msm_host->dlane_swap)); - if (!(flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) + if (!(flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) { + lane_ctrl = dsi_read(msm_host, REG_DSI_LANE_CTRL); + + if (msm_dsi_phy_set_continuous_clock(phy, true)) + lane_ctrl &= ~DSI_LANE_CTRL_HS_REQ_SEL_PHY; + dsi_write(msm_host, REG_DSI_LANE_CTRL, - DSI_LANE_CTRL_CLKLN_HS_FORCE_REQUEST); + lane_ctrl | DSI_LANE_CTRL_CLKLN_HS_FORCE_REQUEST); + } data |= DSI_CTRL_ENABLE; dsi_write(msm_host, REG_DSI_CTRL, data); + + if (msm_host->cphy_mode) + dsi_write(msm_host, REG_DSI_CPHY_MODE_CTRL, BIT(0)); } -static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_dual_dsi) +static void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mode) +{ + struct drm_dsc_config *dsc = msm_host->dsc; + u32 reg, reg_ctrl, reg_ctrl2; + u32 slice_per_intf, total_bytes_per_intf; + u32 pkt_per_line; + u32 eol_byte_num; + u32 bytes_per_pkt; + + /* first calculate dsc parameters and then program + * compress mode registers + */ + slice_per_intf = dsc->slice_count; + + total_bytes_per_intf = dsc->slice_chunk_size * slice_per_intf; + bytes_per_pkt = dsc->slice_chunk_size; /* * slice_per_pkt; */ + + eol_byte_num = total_bytes_per_intf % 3; + + /* + * Typically, pkt_per_line = slice_per_intf * slice_per_pkt. + * + * Since the current driver only supports slice_per_pkt = 1, + * pkt_per_line will be equal to slice per intf for now. + */ + pkt_per_line = slice_per_intf; + + if (is_cmd_mode) /* packet data type */ + reg = DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE(MIPI_DSI_DCS_LONG_WRITE); + else + reg = DSI_VIDEO_COMPRESSION_MODE_CTRL_DATATYPE(MIPI_DSI_COMPRESSED_PIXEL_STREAM); + + /* DSI_VIDEO_COMPRESSION_MODE & DSI_COMMAND_COMPRESSION_MODE + * registers have similar offsets, so for below common code use + * DSI_VIDEO_COMPRESSION_MODE_XXXX for setting bits + * + * pkt_per_line is log2 encoded, >>1 works for supported values (1,2,4) + */ + if (pkt_per_line > 4) + drm_warn_once(msm_host->dev, "pkt_per_line too big"); + reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_PKT_PER_LINE(pkt_per_line >> 1); + reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_EOL_BYTE_NUM(eol_byte_num); + reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_EN; + + if (is_cmd_mode) { + reg_ctrl = dsi_read(msm_host, REG_DSI_COMMAND_COMPRESSION_MODE_CTRL); + reg_ctrl2 = dsi_read(msm_host, REG_DSI_COMMAND_COMPRESSION_MODE_CTRL2); + + reg_ctrl &= ~0xffff; + reg_ctrl |= reg; + + reg_ctrl2 &= ~DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM0_SLICE_WIDTH__MASK; + reg_ctrl2 |= DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM0_SLICE_WIDTH(dsc->slice_chunk_size); + + dsi_write(msm_host, REG_DSI_COMMAND_COMPRESSION_MODE_CTRL, reg_ctrl); + dsi_write(msm_host, REG_DSI_COMMAND_COMPRESSION_MODE_CTRL2, reg_ctrl2); + } else { + reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_WC(bytes_per_pkt); + dsi_write(msm_host, REG_DSI_VIDEO_COMPRESSION_MODE_CTRL, reg); + } +} + +static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) { struct drm_display_mode *mode = msm_host->mode; u32 hs_start = 0, vs_start = 0; /* take sync start as 0 */ @@ -935,17 +971,19 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_dual_dsi) u32 va_end = va_start + mode->vdisplay; u32 hdisplay = mode->hdisplay; u32 wc; + int ret; + bool wide_bus_enabled = msm_dsi_host_is_wide_bus_enabled(&msm_host->base); DBG(""); /* - * For dual DSI mode, the current DRM mode has + * For bonded DSI mode, the current DRM mode has * the complete width of the panel. Since, the complete * panel is driven by two DSI controllers, the horizontal * timings have to be split between the two dsi controllers. * Adjust the DSI host timing values accordingly. */ - if (is_dual_dsi) { + if (is_bonded_dsi) { h_total /= 2; hs_end /= 2; ha_start /= 2; @@ -953,7 +991,57 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_dual_dsi) hdisplay /= 2; } + if (msm_host->dsc) { + struct drm_dsc_config *dsc = msm_host->dsc; + u32 bytes_per_pclk; + + /* update dsc params with timing params */ + if (!dsc || !mode->hdisplay || !mode->vdisplay) { + pr_err("DSI: invalid input: pic_width: %d pic_height: %d\n", + mode->hdisplay, mode->vdisplay); + return; + } + + dsc->pic_width = mode->hdisplay; + dsc->pic_height = mode->vdisplay; + DBG("Mode %dx%d\n", dsc->pic_width, dsc->pic_height); + + /* we do the calculations for dsc parameters here so that + * panel can use these parameters + */ + ret = dsi_populate_dsc_params(msm_host, dsc); + if (ret) + return; + + /* + * DPU sends 3 bytes per pclk cycle to DSI. If widebus is + * enabled, bus width is extended to 6 bytes. + * + * Calculate the number of pclks needed to transmit one line of + * the compressed data. + + * The back/font porch and pulse width are kept intact. For + * VIDEO mode they represent timing parameters rather than + * actual data transfer, see the documentation for + * dsi_adjust_pclk_for_compression(). For CMD mode they are + * unused anyway. + */ + h_total -= hdisplay; + if (wide_bus_enabled && !(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO)) + bytes_per_pclk = 6; + else + bytes_per_pclk = 3; + + hdisplay = DIV_ROUND_UP(msm_dsc_get_bytes_per_line(msm_host->dsc), bytes_per_pclk); + + h_total += hdisplay; + ha_end = ha_start + hdisplay; + } + if (msm_host->mode_flags & MIPI_DSI_MODE_VIDEO) { + if (msm_host->dsc) + dsi_update_dsc_timing(msm_host, false); + dsi_write(msm_host, REG_DSI_ACTIVE_H, DSI_ACTIVE_H_START(ha_start) | DSI_ACTIVE_H_END(ha_end)); @@ -972,30 +1060,63 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_dual_dsi) DSI_ACTIVE_VSYNC_VPOS_START(vs_start) | DSI_ACTIVE_VSYNC_VPOS_END(vs_end)); } else { /* command mode */ - /* image data and 1 byte write_memory_start cmd */ - wc = hdisplay * dsi_get_bpp(msm_host->format) / 8 + 1; + if (msm_host->dsc) + dsi_update_dsc_timing(msm_host, true); - dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM_CTRL, - DSI_CMD_MDP_STREAM_CTRL_WORD_COUNT(wc) | - DSI_CMD_MDP_STREAM_CTRL_VIRTUAL_CHANNEL( + /* image data and 1 byte write_memory_start cmd */ + if (!msm_host->dsc) + wc = hdisplay * mipi_dsi_pixel_format_to_bpp(msm_host->format) / 8 + 1; + else + /* + * When DSC is enabled, WC = slice_chunk_size * slice_per_pkt + 1. + * Currently, the driver only supports default value of slice_per_pkt = 1 + * + * TODO: Expand mipi_dsi_device struct to hold slice_per_pkt info + * and adjust DSC math to account for slice_per_pkt. + */ + wc = msm_host->dsc->slice_chunk_size + 1; + + dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_CTRL, + DSI_CMD_MDP_STREAM0_CTRL_WORD_COUNT(wc) | + DSI_CMD_MDP_STREAM0_CTRL_VIRTUAL_CHANNEL( msm_host->channel) | - DSI_CMD_MDP_STREAM_CTRL_DATA_TYPE( + DSI_CMD_MDP_STREAM0_CTRL_DATA_TYPE( MIPI_DSI_DCS_LONG_WRITE)); - dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM_TOTAL, - DSI_CMD_MDP_STREAM_TOTAL_H_TOTAL(hdisplay) | - DSI_CMD_MDP_STREAM_TOTAL_V_TOTAL(mode->vdisplay)); + dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_TOTAL, + DSI_CMD_MDP_STREAM0_TOTAL_H_TOTAL(hdisplay) | + DSI_CMD_MDP_STREAM0_TOTAL_V_TOTAL(mode->vdisplay)); } } static void dsi_sw_reset(struct msm_dsi_host *msm_host) { + u32 ctrl; + + ctrl = dsi_read(msm_host, REG_DSI_CTRL); + + if (ctrl & DSI_CTRL_ENABLE) { + dsi_write(msm_host, REG_DSI_CTRL, ctrl & ~DSI_CTRL_ENABLE); + /* + * dsi controller need to be disabled before + * clocks turned on + */ + wmb(); + } + dsi_write(msm_host, REG_DSI_CLK_CTRL, DSI_CLK_CTRL_ENABLE_CLKS); wmb(); /* clocks need to be enabled before reset */ + /* dsi controller can only be reset while clocks are running */ dsi_write(msm_host, REG_DSI_RESET, 1); - wmb(); /* make sure reset happen */ + msleep(DSI_RESET_TOGGLE_DELAY_MS); /* make sure reset happen */ dsi_write(msm_host, REG_DSI_RESET, 0); + wmb(); /* controller out of reset */ + + if (ctrl & DSI_CTRL_ENABLE) { + dsi_write(msm_host, REG_DSI_CTRL, ctrl); + wmb(); /* make sure dsi controller enabled again */ + } } static void dsi_op_mode_config(struct msm_dsi_host *msm_host, @@ -1049,7 +1170,7 @@ static void dsi_wait4video_done(struct msm_dsi_host *msm_host) ret = wait_for_completion_timeout(&msm_host->video_comp, msecs_to_jiffies(70)); - if (ret <= 0) + if (ret == 0) DRM_DEV_ERROR(dev, "wait for video done timed out\n"); dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_VIDEO_DONE, 0); @@ -1057,9 +1178,21 @@ static void dsi_wait4video_done(struct msm_dsi_host *msm_host) static void dsi_wait4video_eng_busy(struct msm_dsi_host *msm_host) { + u32 data; + if (!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO)) return; + data = dsi_read(msm_host, REG_DSI_STATUS0); + + /* if video mode engine is not busy, its because + * either timing engine was not turned on or the + * DSI controller has finished transmitting the video + * data already, so no need to wait in those cases + */ + if (!(data & DSI_STATUS0_VIDEO_MODE_ENGINE_BUSY)) + return; + if (msm_host->power_on && msm_host->enabled) { dsi_wait4video_done(msm_host); /* delay 4 ms to skip BLLP */ @@ -1074,8 +1207,10 @@ int dsi_tx_buf_alloc_6g(struct msm_dsi_host *msm_host, int size) uint64_t iova; u8 *data; - data = msm_gem_kernel_new(dev, size, MSM_BO_UNCACHED, - priv->kms->aspace, + msm_host->vm = drm_gpuvm_get(priv->kms->vm); + + data = msm_gem_kernel_new(dev, size, MSM_BO_WC, + msm_host->vm, &msm_host->tx_gem_obj, &iova); if (IS_ERR(data)) { @@ -1104,10 +1239,10 @@ int dsi_tx_buf_alloc_v2(struct msm_dsi_host *msm_host, int size) return 0; } -static void dsi_tx_buf_free(struct msm_dsi_host *msm_host) +void msm_dsi_tx_buf_free(struct mipi_dsi_host *host) { + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); struct drm_device *dev = msm_host->dev; - struct msm_drm_private *priv; /* * This is possible if we're tearing down before we've had a chance to @@ -1118,11 +1253,11 @@ static void dsi_tx_buf_free(struct msm_dsi_host *msm_host) if (!dev) return; - priv = dev->dev_private; if (msm_host->tx_gem_obj) { - msm_gem_unpin_iova(msm_host->tx_gem_obj, priv->kms->aspace); - drm_gem_object_put_unlocked(msm_host->tx_gem_obj); + msm_gem_kernel_put(msm_host->tx_gem_obj, msm_host->vm); + drm_gpuvm_put(msm_host->vm); msm_host->tx_gem_obj = NULL; + msm_host->vm = NULL; } if (msm_host->tx_buf) @@ -1206,14 +1341,15 @@ static int dsi_cmd_dma_add(struct msm_dsi_host *msm_host, static int dsi_short_read1_resp(u8 *buf, const struct mipi_dsi_msg *msg) { u8 *data = msg->rx_buf; + if (data && (msg->rx_len >= 1)) { *data = buf[1]; /* strip out dcs type */ return 1; - } else { - pr_err("%s: read data does not match with rx_buf len %zu\n", - __func__, msg->rx_len); - return -EINVAL; } + + pr_err("%s: read data does not match with rx_buf len %zu\n", + __func__, msg->rx_len); + return -EINVAL; } /* @@ -1222,15 +1358,16 @@ static int dsi_short_read1_resp(u8 *buf, const struct mipi_dsi_msg *msg) static int dsi_short_read2_resp(u8 *buf, const struct mipi_dsi_msg *msg) { u8 *data = msg->rx_buf; + if (data && (msg->rx_len >= 2)) { data[0] = buf[1]; /* strip out dcs type */ data[1] = buf[2]; return 2; - } else { - pr_err("%s: read data does not match with rx_buf len %zu\n", - __func__, msg->rx_len); - return -EINVAL; } + + pr_err("%s: read data does not match with rx_buf len %zu\n", + __func__, msg->rx_len); + return -EINVAL; } static int dsi_long_read_resp(u8 *buf, const struct mipi_dsi_msg *msg) @@ -1251,7 +1388,7 @@ int dsi_dma_base_get_6g(struct msm_dsi_host *msm_host, uint64_t *dma_base) return -EINVAL; return msm_gem_get_and_pin_iova(msm_host->tx_gem_obj, - priv->kms->aspace, dma_base); + priv->kms->vm, dma_base); } int dsi_dma_base_get_v2(struct msm_dsi_host *msm_host, uint64_t *dma_base) @@ -1290,8 +1427,9 @@ static int dsi_cmd_dma_tx(struct msm_dsi_host *msm_host, int len) ret = -ETIMEDOUT; else ret = len; - } else + } else { ret = len; + } return ret; } @@ -1299,14 +1437,13 @@ static int dsi_cmd_dma_tx(struct msm_dsi_host *msm_host, int len) static int dsi_cmd_dma_rx(struct msm_dsi_host *msm_host, u8 *buf, int rx_byte, int pkt_size) { - u32 *lp, *temp, data; + u32 *temp, data; int i, j = 0, cnt; u32 read_cnt; u8 reg[16]; int repeated_bytes = 0; int buf_offset = buf - msm_host->rx_buf; - lp = (u32 *)buf; temp = (u32 *)reg; cnt = (rx_byte + 3) >> 2; if (cnt > 4) @@ -1351,20 +1488,21 @@ static int dsi_cmds2buf_tx(struct msm_dsi_host *msm_host, { int len, ret; int bllp_len = msm_host->mode->hdisplay * - dsi_get_bpp(msm_host->format) / 8; + mipi_dsi_pixel_format_to_bpp(msm_host->format) / 8; len = dsi_cmd_dma_add(msm_host, msg); - if (!len) { + if (len < 0) { pr_err("%s: failed to add cmd type = 0x%x\n", __func__, msg->type); - return -EINVAL; + return len; } - /* for video mode, do not send cmds more than - * one pixel line, since it only transmit it - * during BLLP. - */ - /* TODO: if the command is sent in LP mode, the bit rate is only + /* + * for video mode, do not send cmds more than + * one pixel line, since it only transmit it + * during BLLP. + * + * TODO: if the command is sent in LP mode, the bit rate is only * half of esc clk rate. In this case, if the video is already * actively streaming, we need to check more carefully if the * command can be fit into one BLLP. @@ -1376,49 +1514,19 @@ static int dsi_cmds2buf_tx(struct msm_dsi_host *msm_host, } ret = dsi_cmd_dma_tx(msm_host, len); - if (ret < len) { - pr_err("%s: cmd dma tx failed, type=0x%x, data0=0x%x, len=%d\n", - __func__, msg->type, (*(u8 *)(msg->tx_buf)), len); - return -ECOMM; + if (ret < 0) { + pr_err("%s: cmd dma tx failed, type=0x%x, data0=0x%x, len=%d, ret=%d\n", + __func__, msg->type, (*(u8 *)(msg->tx_buf)), len, ret); + return ret; + } else if (ret < len) { + pr_err("%s: cmd dma tx failed, type=0x%x, data0=0x%x, ret=%d len=%d\n", + __func__, msg->type, (*(u8 *)(msg->tx_buf)), ret, len); + return -EIO; } return len; } -static void dsi_sw_reset_restore(struct msm_dsi_host *msm_host) -{ - u32 data0, data1; - - data0 = dsi_read(msm_host, REG_DSI_CTRL); - data1 = data0; - data1 &= ~DSI_CTRL_ENABLE; - dsi_write(msm_host, REG_DSI_CTRL, data1); - /* - * dsi controller need to be disabled before - * clocks turned on - */ - wmb(); - - dsi_write(msm_host, REG_DSI_CLK_CTRL, DSI_CLK_CTRL_ENABLE_CLKS); - wmb(); /* make sure clocks enabled */ - - /* dsi controller can only be reset while clocks are running */ - dsi_write(msm_host, REG_DSI_RESET, 1); - wmb(); /* make sure reset happen */ - dsi_write(msm_host, REG_DSI_RESET, 0); - wmb(); /* controller out of reset */ - dsi_write(msm_host, REG_DSI_CTRL, data0); - wmb(); /* make sure dsi controller enabled again */ -} - -static void dsi_hpd_worker(struct work_struct *work) -{ - struct msm_dsi_host *msm_host = - container_of(work, struct msm_dsi_host, hpd_work); - - drm_helper_hpd_irq_event(msm_host->dev); -} - static void dsi_err_worker(struct work_struct *work) { struct msm_dsi_host *msm_host = @@ -1427,7 +1535,7 @@ static void dsi_err_worker(struct work_struct *work) pr_err_ratelimited("%s: status=%x\n", __func__, status); if (status & DSI_ERR_STATE_MDP_FIFO_UNDERFLOW) - dsi_sw_reset_restore(msm_host); + dsi_sw_reset(msm_host); /* It is safe to clear here because error irq is disabled. */ msm_host->err_work_state = 0; @@ -1562,28 +1670,6 @@ static irqreturn_t dsi_host_irq(int irq, void *ptr) return IRQ_HANDLED; } -static int dsi_host_init_panel_gpios(struct msm_dsi_host *msm_host, - struct device *panel_device) -{ - msm_host->disp_en_gpio = devm_gpiod_get_optional(panel_device, - "disp-enable", - GPIOD_OUT_LOW); - if (IS_ERR(msm_host->disp_en_gpio)) { - DBG("cannot get disp-enable-gpios %ld", - PTR_ERR(msm_host->disp_en_gpio)); - return PTR_ERR(msm_host->disp_en_gpio); - } - - msm_host->te_gpio = devm_gpiod_get_optional(panel_device, "disp-te", - GPIOD_IN); - if (IS_ERR(msm_host->te_gpio)) { - DBG("cannot get disp-te-gpios %ld", PTR_ERR(msm_host->te_gpio)); - return PTR_ERR(msm_host->te_gpio); - } - - return 0; -} - static int dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_device *dsi) { @@ -1597,17 +1683,14 @@ static int dsi_host_attach(struct mipi_dsi_host *host, msm_host->lanes = dsi->lanes; msm_host->format = dsi->format; msm_host->mode_flags = dsi->mode_flags; + if (dsi->dsc) + msm_host->dsc = dsi->dsc; - msm_dsi_manager_attach_dsi_device(msm_host->id, dsi->mode_flags); - - /* Some gpios defined in panel DT need to be controlled by host */ - ret = dsi_host_init_panel_gpios(msm_host, &dsi->dev); + ret = dsi_dev_attach(msm_host->pdev); if (ret) return ret; DBG("id=%d", msm_host->id); - if (msm_host->dev) - queue_work(msm_host->workqueue, &msm_host->hpd_work); return 0; } @@ -1617,11 +1700,9 @@ static int dsi_host_detach(struct mipi_dsi_host *host, { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); - msm_host->device_node = NULL; + dsi_dev_detach(msm_host->pdev); DBG("id=%d", msm_host->id); - if (msm_host->dev) - queue_work(msm_host->workqueue, &msm_host->hpd_work); return 0; } @@ -1642,7 +1723,7 @@ static ssize_t dsi_host_transfer(struct mipi_dsi_host *host, return ret; } -static struct mipi_dsi_host_ops dsi_host_ops = { +static const struct mipi_dsi_host_ops dsi_host_ops = { .attach = dsi_host_attach, .detach = dsi_host_detach, .transfer = dsi_host_transfer, @@ -1677,14 +1758,15 @@ static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, if (!prop) { DRM_DEV_DEBUG(dev, "failed to find data lane mapping, using default\n"); + /* Set the number of date lanes to 4 by default. */ + msm_host->num_data_lanes = 4; return 0; } - num_lanes = len / sizeof(u32); - - if (num_lanes < 1 || num_lanes > 4) { + num_lanes = drm_of_get_data_lanes_count(ep, 1, 4); + if (num_lanes < 0) { DRM_DEV_ERROR(dev, "bad number of data lanes\n"); - return -EINVAL; + return num_lanes; } msm_host->num_data_lanes = num_lanes; @@ -1729,11 +1811,59 @@ static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, return -EINVAL; } +static int dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc_config *dsc) +{ + int ret; + + if (dsc->bits_per_pixel & 0xf) { + DRM_DEV_ERROR(&msm_host->pdev->dev, "DSI does not support fractional bits_per_pixel\n"); + return -EINVAL; + } + + switch (dsc->bits_per_component) { + case 8: + case 10: + case 12: + /* + * Only 8, 10, and 12 bpc are supported for DSC 1.1 block. + * If additional bpc values need to be supported, update + * this quard with the appropriate DSC version verification. + */ + break; + default: + DRM_DEV_ERROR(&msm_host->pdev->dev, + "Unsupported bits_per_component value: %d\n", + dsc->bits_per_component); + return -EOPNOTSUPP; + } + + dsc->simple_422 = 0; + dsc->convert_rgb = 1; + dsc->vbr_enable = 0; + + drm_dsc_set_const_params(dsc); + drm_dsc_set_rc_buf_thresh(dsc); + + /* DPU supports only pre-SCR panels */ + ret = drm_dsc_setup_rc_params(dsc, DRM_DSC_1_1_PRE_SCR); + if (ret) { + DRM_DEV_ERROR(&msm_host->pdev->dev, "could not find DSC RC parameters\n"); + return ret; + } + + dsc->initial_scale_value = drm_dsc_initial_scale_value(dsc); + dsc->line_buf_depth = dsc->bits_per_component + 1; + + return drm_dsc_compute_rc_parameters(dsc); +} + static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) { + struct msm_dsi *msm_dsi = platform_get_drvdata(msm_host->pdev); struct device *dev = &msm_host->pdev->dev; struct device_node *np = dev->of_node; - struct device_node *endpoint, *device_node; + struct device_node *endpoint; + const char *te_source; int ret = 0; /* @@ -1756,17 +1886,24 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) goto err; } - /* Get panel node from the output port's endpoint data */ - device_node = of_graph_get_remote_node(np, 1, 0); - if (!device_node) { - DRM_DEV_DEBUG(dev, "%s: no valid device\n", __func__); - ret = -ENODEV; + ret = of_property_read_string(endpoint, "qcom,te-source", &te_source); + if (ret && ret != -EINVAL) { + DRM_DEV_ERROR(dev, "%s: invalid TE source configuration %d\n", + __func__, ret); goto err; } + if (!ret) { + msm_dsi->te_source = devm_kstrdup(dev, te_source, GFP_KERNEL); + if (!msm_dsi->te_source) { + DRM_DEV_ERROR(dev, "%s: failed to allocate te_source\n", + __func__); + ret = -ENOMEM; + goto err; + } + } + ret = 0; - msm_host->device_node = device_node; - - if (of_property_read_bool(np, "syscon-sfpb")) { + if (of_property_present(np, "syscon-sfpb")) { msm_host->sfpb = syscon_regmap_lookup_by_phandle(np, "syscon-sfpb"); if (IS_ERR(msm_host->sfpb)) { @@ -1776,8 +1913,6 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) } } - of_node_put(device_node); - err: of_node_put(endpoint); @@ -1789,16 +1924,16 @@ static int dsi_host_get_id(struct msm_dsi_host *msm_host) struct platform_device *pdev = msm_host->pdev; const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; struct resource *res; - int i; + int i, j; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dsi_ctrl"); if (!res) return -EINVAL; - for (i = 0; i < cfg->num_dsi; i++) { - if (cfg->io_start[i] == res->start) - return i; - } + for (i = 0; i < VARIANTS_MAX; i++) + for (j = 0; j < DSI_MAX; j++) + if (cfg->io_start[i][j] == res->start) + return j; return -EINVAL; } @@ -1807,69 +1942,76 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) { struct msm_dsi_host *msm_host = NULL; struct platform_device *pdev = msm_dsi->pdev; + const struct msm_dsi_config *cfg; int ret; msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL); - if (!msm_host) { - pr_err("%s: FAILED: cannot alloc dsi host\n", - __func__); - ret = -ENOMEM; - goto fail; - } + if (!msm_host) + return -ENOMEM; msm_host->pdev = pdev; msm_dsi->host = &msm_host->base; ret = dsi_host_parse_dt(msm_host); - if (ret) { - pr_err("%s: failed to parse dt\n", __func__); - goto fail; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "%s: failed to parse dt\n", + __func__); - 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__); - ret = PTR_ERR(msm_host->ctrl_base); - goto fail; - } + msm_host->ctrl_base = msm_ioremap_size(pdev, "dsi_ctrl", &msm_host->ctrl_size); + if (IS_ERR(msm_host->ctrl_base)) + return dev_err_probe(&pdev->dev, PTR_ERR(msm_host->ctrl_base), + "%s: unable to map Dsi ctrl base\n", __func__); pm_runtime_enable(&pdev->dev); msm_host->cfg_hnd = dsi_get_config(msm_host); - if (!msm_host->cfg_hnd) { - ret = -EINVAL; - pr_err("%s: get config failed\n", __func__); - goto fail; - } + if (!msm_host->cfg_hnd) + return dev_err_probe(&pdev->dev, -EINVAL, + "%s: get config failed\n", __func__); + cfg = msm_host->cfg_hnd->cfg; msm_host->id = dsi_host_get_id(msm_host); - if (msm_host->id < 0) { - ret = msm_host->id; - pr_err("%s: unable to identify DSI host index\n", __func__); - goto fail; - } + if (msm_host->id < 0) + return dev_err_probe(&pdev->dev, msm_host->id, + "%s: unable to identify DSI host index\n", + __func__); /* fixup base address by io offset */ - msm_host->ctrl_base += msm_host->cfg_hnd->cfg->io_offset; + msm_host->ctrl_base += cfg->io_offset; - ret = dsi_regulator_init(msm_host); - if (ret) { - pr_err("%s: regulator init failed\n", __func__); - goto fail; - } + ret = devm_regulator_bulk_get_const(&pdev->dev, cfg->num_regulators, + cfg->regulator_data, + &msm_host->supplies); + if (ret) + return ret; ret = dsi_clk_init(msm_host); - if (ret) { - pr_err("%s: unable to initialize dsi clks\n", __func__); - goto fail; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "%s: unable to initialize dsi clks\n", __func__); msm_host->rx_buf = devm_kzalloc(&pdev->dev, SZ_4K, GFP_KERNEL); - if (!msm_host->rx_buf) { - ret = -ENOMEM; - pr_err("%s: alloc rx temp buf failed\n", __func__); - goto fail; - } + if (!msm_host->rx_buf) + return -ENOMEM; + + ret = devm_pm_opp_set_clkname(&pdev->dev, "byte"); + if (ret) + return ret; + /* OPP table is optional */ + ret = devm_pm_opp_of_add_table(&pdev->dev); + if (ret && ret != -ENODEV) + return dev_err_probe(&pdev->dev, ret, "invalid OPP table in device tree\n"); + + msm_host->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); + if (!msm_host->irq) + return dev_err_probe(&pdev->dev, -EINVAL, "failed to get irq\n"); + + /* do not autoenable, will be enabled later */ + ret = devm_request_irq(&pdev->dev, msm_host->irq, dsi_host_irq, + IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, + "dsi_isr", msm_host); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed to request IRQ%u\n", + msm_host->irq); init_completion(&msm_host->dma_comp); init_completion(&msm_host->video_comp); @@ -1879,16 +2021,15 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) /* setup workqueue */ msm_host->workqueue = alloc_ordered_workqueue("dsi_drm_work", 0); + if (!msm_host->workqueue) + return -ENOMEM; + INIT_WORK(&msm_host->err_work, dsi_err_worker); - INIT_WORK(&msm_host->hpd_work, dsi_hpd_worker); msm_dsi->id = msm_host->id; DBG("Dsi Host %d initialized", msm_host->id); return 0; - -fail: - return ret; } void msm_dsi_host_destroy(struct mipi_dsi_host *host) @@ -1896,9 +2037,7 @@ void msm_dsi_host_destroy(struct mipi_dsi_host *host) struct msm_dsi_host *msm_host = to_msm_dsi_host(host); DBG(""); - dsi_tx_buf_free(msm_host); if (msm_host->workqueue) { - flush_workqueue(msm_host->workqueue); destroy_workqueue(msm_host->workqueue); msm_host->workqueue = NULL; } @@ -1914,26 +2053,10 @@ int msm_dsi_host_modeset_init(struct mipi_dsi_host *host, { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; - struct platform_device *pdev = msm_host->pdev; int ret; - msm_host->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); - if (msm_host->irq < 0) { - ret = msm_host->irq; - DRM_DEV_ERROR(dev->dev, "failed to get irq: %d\n", ret); - return ret; - } - - ret = devm_request_irq(&pdev->dev, msm_host->irq, - dsi_host_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, - "dsi_isr", msm_host); - if (ret < 0) { - DRM_DEV_ERROR(&pdev->dev, "failed to request IRQ%u: %d\n", - msm_host->irq, ret); - return ret; - } - msm_host->dev = dev; + ret = cfg_hnd->ops->tx_buf_alloc(msm_host, SZ_4K); if (ret) { pr_err("%s: alloc tx gem obj failed, %d\n", __func__, ret); @@ -1943,7 +2066,7 @@ int msm_dsi_host_modeset_init(struct mipi_dsi_host *host, return 0; } -int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer) +int msm_dsi_host_register(struct mipi_dsi_host *host) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); int ret; @@ -1957,20 +2080,6 @@ int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer) return ret; msm_host->registered = true; - - /* If the panel driver has not been probed after host register, - * we should defer the host's probe. - * It makes sure panel is connected when fbcon detects - * connector status and gets the proper display mode to - * create framebuffer. - * Don't try to defer if there is nothing connected to the dsi - * output - */ - if (check_defer && msm_host->device_node) { - if (IS_ERR(of_drm_find_panel(msm_host->device_node))) - if (!of_drm_find_bridge(msm_host->device_node)) - return -EPROBE_DEFER; - } } return 0; @@ -2005,6 +2114,7 @@ int msm_dsi_host_xfer_prepare(struct mipi_dsi_host *host, * mdp clock need to be enabled to receive dsi interrupt */ pm_runtime_get_sync(&msm_host->pdev->dev); + cfg_hnd->ops->link_clk_set_rate(msm_host); cfg_hnd->ops->link_clk_enable(msm_host); /* TODO: vote for bus bandwidth */ @@ -2037,7 +2147,7 @@ void msm_dsi_host_xfer_restore(struct mipi_dsi_host *host, /* TODO: unvote for bus bandwidth */ cfg_hnd->ops->link_clk_disable(msm_host); - pm_runtime_put_autosuspend(&msm_host->pdev->dev); + pm_runtime_put(&msm_host->pdev->dev); } int msm_dsi_host_cmd_tx(struct mipi_dsi_host *host, @@ -2105,9 +2215,12 @@ int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host, } ret = dsi_cmds2buf_tx(msm_host, msg); - if (ret < msg->tx_len) { + if (ret < 0) { pr_err("%s: Read cmd Tx failed, %d\n", __func__, ret); return ret; + } else if (ret < msg->tx_len) { + pr_err("%s: Read cmd Tx failed, too short: %d\n", __func__, ret); + return -ECOMM; } /* @@ -2196,55 +2309,12 @@ void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 dma_base, wmb(); } -int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host, - struct msm_dsi_pll *src_pll) +void msm_dsi_host_set_phy_mode(struct mipi_dsi_host *host, + struct msm_dsi_phy *src_phy) { 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; - } - - if (msm_host->dsi_clk_src) { - 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; - } - } - - if (msm_host->esc_clk_src) { - 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; + msm_host->cphy_mode = src_phy->cphy_mode; } void msm_dsi_host_reset_phy(struct mipi_dsi_host *host) @@ -2262,22 +2332,43 @@ void msm_dsi_host_reset_phy(struct mipi_dsi_host *host) void msm_dsi_host_get_phy_clk_req(struct mipi_dsi_host *host, struct msm_dsi_phy_clk_request *clk_req, - bool is_dual_dsi) + bool is_bonded_dsi) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; int ret; - ret = cfg_hnd->ops->calc_clk_rate(msm_host, is_dual_dsi); + ret = cfg_hnd->ops->calc_clk_rate(msm_host, is_bonded_dsi); if (ret) { pr_err("%s: unable to calc clk rate, %d\n", __func__, ret); return; } - clk_req->bitclk_rate = msm_host->byte_clk_rate * 8; + /* CPHY transmits 16 bits over 7 clock cycles + * "byte_clk" is in units of 16-bits (see dsi_calc_pclk), + * so multiply by 7 to get the "bitclk rate" + */ + if (msm_host->cphy_mode) + clk_req->bitclk_rate = msm_host->byte_clk_rate * 7; + else + clk_req->bitclk_rate = msm_host->byte_clk_rate * 8; clk_req->escclk_rate = msm_host->esc_clk_rate; } +void msm_dsi_host_enable_irq(struct mipi_dsi_host *host) +{ + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + + enable_irq(msm_host->irq); +} + +void msm_dsi_host_disable_irq(struct mipi_dsi_host *host) +{ + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + + disable_irq(msm_host->irq); +} + int msm_dsi_host_enable(struct mipi_dsi_host *host) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); @@ -2291,7 +2382,7 @@ int msm_dsi_host_enable(struct mipi_dsi_host *host) */ /* if (msm_panel->mode == MSM_DSI_CMD_MODE) { * dsi_link_clk_disable(msm_host); - * pm_runtime_put_autosuspend(&msm_host->pdev->dev); + * pm_runtime_put(&msm_host->pdev->dev); * } */ msm_host->enabled = true; @@ -2331,7 +2422,7 @@ static void msm_dsi_sfpb_config(struct msm_dsi_host *msm_host, bool enable) int msm_dsi_host_power_on(struct mipi_dsi_host *host, struct msm_dsi_phy_shared_timings *phy_shared_timings, - bool is_dual_dsi) + bool is_bonded_dsi, struct msm_dsi_phy *phy) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; @@ -2343,9 +2434,14 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host, goto unlock_ret; } + msm_host->byte_intf_clk_rate = msm_host->byte_clk_rate; + if (phy_shared_timings->byte_intf_clk_div_2) + msm_host->byte_intf_clk_rate /= 2; + msm_dsi_sfpb_config(msm_host, true); - ret = dsi_host_regulator_enable(msm_host); + ret = regulator_bulk_enable(msm_host->cfg_hnd->cfg->num_regulators, + msm_host->supplies); if (ret) { pr_err("%s:Failed to enable vregs.ret=%d\n", __func__, ret); @@ -2353,7 +2449,9 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host, } pm_runtime_get_sync(&msm_host->pdev->dev); - ret = cfg_hnd->ops->link_clk_enable(msm_host); + ret = cfg_hnd->ops->link_clk_set_rate(msm_host); + if (!ret) + ret = cfg_hnd->ops->link_clk_enable(msm_host); if (ret) { pr_err("%s: failed to enable link clocks. ret=%d\n", __func__, ret); @@ -2367,12 +2465,9 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host, goto fail_disable_clk; } - dsi_timing_setup(msm_host, is_dual_dsi); + dsi_timing_setup(msm_host, is_bonded_dsi); dsi_sw_reset(msm_host); - dsi_ctrl_config(msm_host, true, phy_shared_timings); - - if (msm_host->disp_en_gpio) - gpiod_set_value(msm_host->disp_en_gpio, 1); + dsi_ctrl_enable(msm_host, phy_shared_timings, phy); msm_host->power_on = true; mutex_unlock(&msm_host->dev_mutex); @@ -2381,9 +2476,10 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host, fail_disable_clk: cfg_hnd->ops->link_clk_disable(msm_host); - pm_runtime_put_autosuspend(&msm_host->pdev->dev); + pm_runtime_put(&msm_host->pdev->dev); fail_disable_reg: - dsi_host_regulator_disable(msm_host); + regulator_bulk_disable(msm_host->cfg_hnd->cfg->num_regulators, + msm_host->supplies); unlock_ret: mutex_unlock(&msm_host->dev_mutex); return ret; @@ -2400,17 +2496,15 @@ int msm_dsi_host_power_off(struct mipi_dsi_host *host) goto unlock_ret; } - dsi_ctrl_config(msm_host, false, NULL); - - if (msm_host->disp_en_gpio) - gpiod_set_value(msm_host->disp_en_gpio, 0); + dsi_ctrl_disable(msm_host); pinctrl_pm_select_sleep_state(&msm_host->pdev->dev); cfg_hnd->ops->link_clk_disable(msm_host); - pm_runtime_put_autosuspend(&msm_host->pdev->dev); + pm_runtime_put(&msm_host->pdev->dev); - dsi_host_regulator_disable(msm_host); + regulator_bulk_disable(msm_host->cfg_hnd->cfg->num_regulators, + msm_host->supplies); msm_dsi_sfpb_config(msm_host, false); @@ -2424,7 +2518,7 @@ unlock_ret: } int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, - struct drm_display_mode *mode) + const struct drm_display_mode *mode) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); @@ -2442,22 +2536,113 @@ int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, return 0; } -struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host, - unsigned long *panel_flags) +enum drm_mode_status msm_dsi_host_check_dsc(struct mipi_dsi_host *host, + const struct drm_display_mode *mode) +{ + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + struct drm_dsc_config *dsc = msm_host->dsc; + int pic_width = mode->hdisplay; + int pic_height = mode->vdisplay; + + if (!msm_host->dsc) + return MODE_OK; + + if (pic_width % dsc->slice_width) { + pr_err("DSI: pic_width %d has to be multiple of slice %d\n", + pic_width, dsc->slice_width); + return MODE_H_ILLEGAL; + } + + if (pic_height % dsc->slice_height) { + pr_err("DSI: pic_height %d has to be multiple of slice %d\n", + pic_height, dsc->slice_height); + return MODE_V_ILLEGAL; + } + + return MODE_OK; +} + +unsigned long msm_dsi_host_get_mode_flags(struct mipi_dsi_host *host) +{ + return to_msm_dsi_host(host)->mode_flags; +} + +void msm_dsi_host_snapshot(struct msm_disp_state *disp_state, struct mipi_dsi_host *host) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); - struct drm_panel *panel; - panel = of_drm_find_panel(msm_host->device_node); - if (panel_flags) - *panel_flags = msm_host->mode_flags; + pm_runtime_get_sync(&msm_host->pdev->dev); + + msm_disp_snapshot_add_block(disp_state, msm_host->ctrl_size, + msm_host->ctrl_base, "dsi%d_ctrl", msm_host->id); + + pm_runtime_put_sync(&msm_host->pdev->dev); +} + +static void msm_dsi_host_video_test_pattern_setup(struct msm_dsi_host *msm_host) +{ + u32 reg; + + reg = dsi_read(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL); + + dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_VIDEO_INIT_VAL, 0xff); + /* draw checkered rectangle pattern */ + dsi_write(msm_host, REG_DSI_TPG_MAIN_CONTROL, + DSI_TPG_MAIN_CONTROL_CHECKERED_RECTANGLE_PATTERN); + /* use 24-bit RGB test pttern */ + dsi_write(msm_host, REG_DSI_TPG_VIDEO_CONFIG, + DSI_TPG_VIDEO_CONFIG_BPP(VIDEO_CONFIG_24BPP) | + DSI_TPG_VIDEO_CONFIG_RGB); + + reg |= DSI_TEST_PATTERN_GEN_CTRL_VIDEO_PATTERN_SEL(VID_MDSS_GENERAL_PATTERN); + dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL, reg); + + DBG("Video test pattern setup done\n"); +} + +static void msm_dsi_host_cmd_test_pattern_setup(struct msm_dsi_host *msm_host) +{ + u32 reg; + + reg = dsi_read(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL); + + /* initial value for test pattern */ + dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CMD_MDP_INIT_VAL0, 0xff); + + reg |= DSI_TEST_PATTERN_GEN_CTRL_CMD_MDP_STREAM0_PATTERN_SEL(CMD_MDP_MDSS_GENERAL_PATTERN); + + dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL, reg); + /* draw checkered rectangle pattern */ + dsi_write(msm_host, REG_DSI_TPG_MAIN_CONTROL2, + DSI_TPG_MAIN_CONTROL2_CMD_MDP0_CHECKERED_RECTANGLE_PATTERN); + + DBG("Cmd test pattern setup done\n"); +} + +void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host) +{ + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + bool is_video_mode = !!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO); + u32 reg; + + if (is_video_mode) + msm_dsi_host_video_test_pattern_setup(msm_host); + else + msm_dsi_host_cmd_test_pattern_setup(msm_host); + + reg = dsi_read(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL); + /* enable the test pattern generator */ + dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL, (reg | DSI_TEST_PATTERN_GEN_CTRL_EN)); - return panel; + /* for command mode need to trigger one frame from tpg */ + if (!is_video_mode) + dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER, + DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER_SW_TRIGGER); } -struct drm_bridge *msm_dsi_host_get_bridge(struct mipi_dsi_host *host) +struct drm_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); - return of_drm_find_bridge(msm_host->device_node); + return msm_host->dsc; } |
