|
@@ -24,26 +24,36 @@
|
|
|
#include <linux/of_graph.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 "dsi.h"
|
|
|
#include "dsi.xml.h"
|
|
|
+#include "sfpb.xml.h"
|
|
|
#include "dsi_cfg.h"
|
|
|
|
|
|
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 +64,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;
|
|
@@ -91,10 +106,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;
|
|
@@ -102,6 +116,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;
|
|
@@ -119,9 +141,19 @@ 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 regmap *sfpb;
|
|
|
+
|
|
|
struct drm_display_mode *mode;
|
|
|
|
|
|
/* connected device info */
|
|
@@ -165,21 +197,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;
|
|
@@ -196,9 +238,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:
|
|
@@ -295,40 +339,23 @@ 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_cfg_handler *cfg_hnd = msm_host->cfg_hnd;
|
|
|
+ const struct msm_dsi_config *cfg = 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);
|
|
@@ -356,80 +383,85 @@ 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;
|
|
|
}
|
|
|
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
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)
|
|
|
+static int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host)
|
|
|
{
|
|
|
int ret;
|
|
|
|
|
@@ -476,11 +508,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)
|
|
@@ -515,6 +634,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;
|
|
@@ -534,6 +654,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;
|
|
|
}
|
|
|
|
|
@@ -835,29 +996,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);
|
|
|
mutex_unlock(&dev->struct_mutex);
|
|
|
- return ret;
|
|
|
- }
|
|
|
+ if (ret) {
|
|
|
+ pr_err("%s: failed to get iova, %d\n", __func__, ret);
|
|
|
+ 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;
|
|
@@ -874,14 +1052,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;
|
|
@@ -894,17 +1077,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 */
|
|
@@ -970,17 +1156,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);
|
|
@@ -988,7 +1178,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));
|
|
@@ -1060,7 +1250,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);
|
|
@@ -1383,6 +1573,16 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host)
|
|
|
|
|
|
msm_host->device_node = device_node;
|
|
|
|
|
|
+ if (of_property_read_bool(np, "syscon-sfpb")) {
|
|
|
+ msm_host->sfpb = syscon_regmap_lookup_by_phandle(np,
|
|
|
+ "syscon-sfpb");
|
|
|
+ if (IS_ERR(msm_host->sfpb)) {
|
|
|
+ dev_err(dev, "%s: failed to get sfpb regmap\n",
|
|
|
+ __func__);
|
|
|
+ return PTR_ERR(msm_host->sfpb);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1408,12 +1608,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__);
|
|
@@ -1437,6 +1631,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__);
|
|
@@ -1750,11 +1950,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);
|
|
|
|
|
@@ -1766,6 +1967,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;
|
|
|
|
|
@@ -1791,6 +1993,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;
|
|
|
}
|
|
@@ -1828,6 +2046,20 @@ int msm_dsi_host_disable(struct mipi_dsi_host *host)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void msm_dsi_sfpb_config(struct msm_dsi_host *msm_host, bool enable)
|
|
|
+{
|
|
|
+ enum sfpb_ahb_arb_master_port_en en;
|
|
|
+
|
|
|
+ if (!msm_host->sfpb)
|
|
|
+ return;
|
|
|
+
|
|
|
+ en = enable ? SFPB_MASTER_PORT_ENABLE : SFPB_MASTER_PORT_DISABLE;
|
|
|
+
|
|
|
+ regmap_update_bits(msm_host->sfpb, REG_SFPB_GPREG,
|
|
|
+ SFPB_GPREG_MASTER_PORT_EN__MASK,
|
|
|
+ SFPB_GPREG_MASTER_PORT_EN(en));
|
|
|
+}
|
|
|
+
|
|
|
int msm_dsi_host_power_on(struct mipi_dsi_host *host)
|
|
|
{
|
|
|
struct msm_dsi_host *msm_host = to_msm_dsi_host(host);
|
|
@@ -1840,6 +2072,8 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host)
|
|
|
goto unlock_ret;
|
|
|
}
|
|
|
|
|
|
+ msm_dsi_sfpb_config(msm_host, true);
|
|
|
+
|
|
|
ret = dsi_calc_clk_rate(msm_host);
|
|
|
if (ret) {
|
|
|
pr_err("%s: unable to calc clk rate, %d\n", __func__, ret);
|
|
@@ -1862,7 +2096,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) {
|
|
@@ -1927,6 +2161,8 @@ int msm_dsi_host_power_off(struct mipi_dsi_host *host)
|
|
|
|
|
|
dsi_host_regulator_disable(msm_host);
|
|
|
|
|
|
+ msm_dsi_sfpb_config(msm_host, false);
|
|
|
+
|
|
|
DBG("-");
|
|
|
|
|
|
msm_host->power_on = false;
|