summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYe Li <ye.li@nxp.com>2020-05-09 03:17:26 -0700
committerYe Li <ye.li@nxp.com>2020-05-11 03:22:46 -0700
commita2fd185001da15fed802de2b96d0420c3c18f749 (patch)
treed07c6930f371b713e7fff3fef807cf481b87f0f9
parent31294b932df3ea21ef204a5077b4c8cef62cdd85 (diff)
MLK-23964-15 reset: Add reset driver for iMX8M display mix
iMX8M dispmix uses GPR registers for display modules in the mix. Implement a reset driver to use DTB Signed-off-by: Ye Li <ye.li@nxp.com> (cherry picked from commit a981b518f357ced460fd87445b327aedadc86ee0)
-rw-r--r--drivers/reset/Kconfig6
-rw-r--r--drivers/reset/Makefile1
-rw-r--r--drivers/reset/reset-dispmix.c290
-rw-r--r--drivers/reset/reset-uclass.c37
-rw-r--r--include/reset.h2
5 files changed, 335 insertions, 1 deletions
diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index 75ccd657997..981de1eb2e0 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -148,4 +148,10 @@ config RESET_IMX7
help
Support for reset controller on i.MX7/8 SoCs.
+config RESET_DISPMIX
+ bool "i.MX8M Display MIX Reset Driver"
+ depends on DM_RESET && ARCH_IMX8M && DM_VIDEO
+ default n
+ help
+ Support for reset controller on i.MX8M SoCs.
endmenu
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 0a044d5d8c8..84f5666232a 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -23,3 +23,4 @@ obj-$(CONFIG_RESET_MTMIPS) += reset-mtmips.o
obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
obj-$(CONFIG_RESET_HISILICON) += reset-hisilicon.o
obj-$(CONFIG_RESET_IMX7) += reset-imx7.o
+obj-$(CONFIG_RESET_DISPMIX) += reset-dispmix.o
diff --git a/drivers/reset/reset-dispmix.c b/drivers/reset/reset-dispmix.c
new file mode 100644
index 00000000000..0d793525b5d
--- /dev/null
+++ b/drivers/reset/reset-dispmix.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2019 NXP
+ *
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <regmap.h>
+#include <reset.h>
+#include <reset-uclass.h>
+#include <regmap.h>
+#include <dt-bindings/reset/imx8mm-dispmix.h>
+#include <dt-bindings/reset/imx8mn-dispmix.h>
+
+/* DISPMIX GPR registers */
+#define DISPLAY_MIX_SFT_RSTN_CSR 0x00
+#define DISPLAY_MIX_CLK_EN_CSR 0x00
+#define GPR_MIPI_RESET_DIV 0x00
+
+struct dispmix_reset_priv {
+ struct regmap *map;
+ bool active_low;
+};
+
+struct dispmix_reset_entry {
+ uint32_t reg_off;
+ uint32_t bit_off;
+};
+
+struct dispmix_reset_drvdata {
+ const struct dispmix_reset_entry *resets;
+ ulong nr_resets;
+};
+
+#define RESET_ENTRY(id, reg, bit) \
+ [id] = { .reg_off = (reg), .bit_off = (bit) }
+
+static const struct dispmix_reset_entry imx8mm_sft_rstn[] = {
+ /* dispmix reset entry */
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_CHIP_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 0),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_IPG_HARD_ASYNC_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 1),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_CSI_HRESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 2),
+ RESET_ENTRY(IMX8MM_CAMERA_PIXEL_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 3),
+ RESET_ENTRY(IMX8MM_MIPI_CSI_I_PRESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 4),
+ RESET_ENTRY(IMX8MM_MIPI_DSI_I_PRESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 5),
+ RESET_ENTRY(IMX8MM_BUS_RSTN_BLK_SYNC,
+ DISPLAY_MIX_SFT_RSTN_CSR, 6),
+};
+
+static const struct dispmix_reset_entry imx8mm_clk_en[] = {
+ /* dispmix clock enable entry */
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_CSI_HCLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 0),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_SPU_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 1),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_MEM_WRAPPER_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 2),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_IPG_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 3),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_IPG_CLK_S_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 4),
+ RESET_ENTRY(IMX8MM_CSI_BRIDGE_IPG_CLK_S_RAW_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 5),
+ RESET_ENTRY(IMX8MM_LCDIF_APB_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 6),
+ RESET_ENTRY(IMX8MM_LCDIF_PIXEL_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 7),
+ RESET_ENTRY(IMX8MM_MIPI_DSI_PCLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 8),
+ RESET_ENTRY(IMX8MM_MIPI_DSI_CLKREF_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 9),
+ RESET_ENTRY(IMX8MM_MIPI_CSI_ACLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 10),
+ RESET_ENTRY(IMX8MM_MIPI_CSI_PCLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 11),
+ RESET_ENTRY(IMX8MM_BUS_BLK_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 12),
+};
+
+static const struct dispmix_reset_entry imx8mm_mipi_rst[] = {
+ /* mipi lanes reset entry */
+ RESET_ENTRY(IMX8MM_MIPI_S_RESET,
+ GPR_MIPI_RESET_DIV, 16),
+ RESET_ENTRY(IMX8MM_MIPI_M_RESET,
+ GPR_MIPI_RESET_DIV, 17),
+};
+
+static const struct dispmix_reset_entry imx8mn_sft_rstn[] = {
+ /* dispmix reset entry */
+ RESET_ENTRY(IMX8MN_MIPI_DSI_PCLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 0),
+ RESET_ENTRY(IMX8MN_MIPI_DSI_CLKREF_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 1),
+ RESET_ENTRY(IMX8MN_MIPI_CSI_PCLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 2),
+ RESET_ENTRY(IMX8MN_MIPI_CSI_ACLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 3),
+ RESET_ENTRY(IMX8MN_LCDIF_PIXEL_CLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 4),
+ RESET_ENTRY(IMX8MN_LCDIF_APB_CLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 5),
+ RESET_ENTRY(IMX8MN_ISI_PROC_CLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 6),
+ RESET_ENTRY(IMX8MN_ISI_APB_CLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 7),
+ RESET_ENTRY(IMX8MN_BUS_BLK_CLK_RESET,
+ DISPLAY_MIX_SFT_RSTN_CSR, 8),
+};
+
+static const struct dispmix_reset_entry imx8mn_clk_en[] = {
+ /* dispmix clock enable entry */
+ RESET_ENTRY(IMX8MN_MIPI_DSI_PCLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 0),
+ RESET_ENTRY(IMX8MN_MIPI_DSI_CLKREF_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 1),
+ RESET_ENTRY(IMX8MN_MIPI_CSI_PCLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 2),
+ RESET_ENTRY(IMX8MN_MIPI_CSI_ACLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 3),
+ RESET_ENTRY(IMX8MN_LCDIF_PIXEL_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 4),
+ RESET_ENTRY(IMX8MN_LCDIF_APB_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 5),
+ RESET_ENTRY(IMX8MN_ISI_PROC_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 6),
+ RESET_ENTRY(IMX8MN_ISI_APB_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 7),
+ RESET_ENTRY(IMX8MN_BUS_BLK_CLK_EN,
+ DISPLAY_MIX_CLK_EN_CSR, 8),
+};
+
+static const struct dispmix_reset_entry imx8mn_mipi_rst[] = {
+ /* mipi lanes reset entry */
+ RESET_ENTRY(IMX8MN_MIPI_S_RESET,
+ GPR_MIPI_RESET_DIV, 16),
+ RESET_ENTRY(IMX8MN_MIPI_M_RESET,
+ GPR_MIPI_RESET_DIV, 17),
+};
+
+static const struct dispmix_reset_drvdata imx8mm_sft_rstn_pdata = {
+ .resets = imx8mm_sft_rstn,
+ .nr_resets = IMX8MM_DISPMIX_SFT_RSTN_NUM,
+};
+
+static const struct dispmix_reset_drvdata imx8mm_clk_en_pdata = {
+ .resets = imx8mm_clk_en,
+ .nr_resets = IMX8MM_DISPMIX_CLK_EN_NUM,
+};
+
+static const struct dispmix_reset_drvdata imx8mm_mipi_rst_pdata = {
+ .resets = imx8mm_mipi_rst,
+ .nr_resets = IMX8MM_MIPI_RESET_NUM,
+};
+
+static const struct dispmix_reset_drvdata imx8mn_sft_rstn_pdata = {
+ .resets = imx8mn_sft_rstn,
+ .nr_resets = IMX8MN_DISPMIX_SFT_RSTN_NUM,
+};
+
+static const struct dispmix_reset_drvdata imx8mn_clk_en_pdata = {
+ .resets = imx8mn_clk_en,
+ .nr_resets = IMX8MN_DISPMIX_CLK_EN_NUM,
+};
+
+static const struct dispmix_reset_drvdata imx8mn_mipi_rst_pdata = {
+ .resets = imx8mn_mipi_rst,
+ .nr_resets = IMX8MN_MIPI_RESET_NUM,
+};
+
+static const struct udevice_id dispmix_reset_dt_ids[] = {
+ {
+ .compatible = "fsl,imx8mm-dispmix-sft-rstn",
+ .data = (ulong)&imx8mm_sft_rstn_pdata,
+ },
+ {
+ .compatible = "fsl,imx8mm-dispmix-clk-en",
+ .data = (ulong)&imx8mm_clk_en_pdata,
+ },
+ {
+ .compatible = "fsl,imx8mm-dispmix-mipi-rst",
+ .data = (ulong)&imx8mm_mipi_rst_pdata,
+ },
+ {
+ .compatible = "fsl,imx8mn-dispmix-sft-rstn",
+ .data = (ulong)&imx8mn_sft_rstn_pdata,
+ },
+ {
+ .compatible = "fsl,imx8mn-dispmix-clk-en",
+ .data = (ulong)&imx8mn_clk_en_pdata,
+ },
+ {
+ .compatible = "fsl,imx8mn-dispmix-mipi-rst",
+ .data = (ulong)&imx8mn_mipi_rst_pdata,
+ },
+ { /* sentinel */ }
+};
+
+static int dispmix_reset_assert(struct reset_ctl *rst)
+{
+ const struct dispmix_reset_entry *rstent;
+ struct dispmix_reset_priv *priv = (struct dispmix_reset_priv *)dev_get_priv(rst->dev);
+ const struct dispmix_reset_drvdata *drvdata = (const struct dispmix_reset_drvdata *)dev_get_driver_data(rst->dev);
+
+
+ if (rst->id >= drvdata->nr_resets) {
+ pr_info("dispmix reset: %lu is not a valid line\n", rst->id);
+ return -EINVAL;
+ }
+
+ rstent = &drvdata->resets[rst->id];
+
+ regmap_update_bits(priv->map, rstent->reg_off,
+ 1 << rstent->bit_off,
+ !priv->active_low << rstent->bit_off);
+
+ return 0;
+}
+
+static int dispmix_reset_deassert(struct reset_ctl *rst)
+{
+ const struct dispmix_reset_entry *rstent;
+ struct dispmix_reset_priv *priv = (struct dispmix_reset_priv *)dev_get_priv(rst->dev);
+ const struct dispmix_reset_drvdata *drvdata =
+ (const struct dispmix_reset_drvdata *)dev_get_driver_data(rst->dev);
+
+
+ if (rst->id >= drvdata->nr_resets) {
+ pr_info("dispmix reset: %lu is not a valid line\n", rst->id);
+ return -EINVAL;
+ }
+
+ rstent = &drvdata->resets[rst->id];
+
+ regmap_update_bits(priv->map, rstent->reg_off,
+ 1 << rstent->bit_off,
+ !!priv->active_low << rstent->bit_off);
+
+ return 0;
+}
+
+static int dispmix_reset_free(struct reset_ctl *rst)
+{
+ return 0;
+}
+
+static int dispmix_reset_request(struct reset_ctl *rst)
+{
+ return 0;
+}
+
+static const struct reset_ops dispmix_reset_ops = {
+ .request = dispmix_reset_request,
+ .rfree = dispmix_reset_free,
+ .rst_assert = dispmix_reset_assert,
+ .rst_deassert = dispmix_reset_deassert,
+};
+
+static int dispmix_reset_probe(struct udevice *dev)
+{
+ struct dispmix_reset_priv *priv = (struct dispmix_reset_priv *)dev_get_priv(dev);
+ int ret;
+
+ priv->active_low = dev_read_bool(dev, "active_low");
+
+ ret = regmap_init_mem(dev_ofnode(dev), &priv->map);
+ if (ret) {
+ debug("%s: Could not initialize regmap (err = %d)\n", dev->name,
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+U_BOOT_DRIVER(dispmix_reset) = {
+ .name = "dispmix_reset",
+ .id = UCLASS_RESET,
+ .of_match = dispmix_reset_dt_ids,
+ .ops = &dispmix_reset_ops,
+ .probe = dispmix_reset_probe,
+ .priv_auto_alloc_size = sizeof(struct dispmix_reset_priv),
+};
diff --git a/drivers/reset/reset-uclass.c b/drivers/reset/reset-uclass.c
index 8ec8e462e62..984efdab0fe 100644
--- a/drivers/reset/reset-uclass.c
+++ b/drivers/reset/reset-uclass.c
@@ -102,7 +102,7 @@ int reset_get_by_index_nodev(ofnode node, int index,
int reset_get_bulk(struct udevice *dev, struct reset_ctl_bulk *bulk)
{
int i, ret, err, count;
-
+
bulk->count = 0;
count = dev_count_phandle_with_args(dev, "resets", "#reset-cells");
@@ -133,6 +133,41 @@ bulk_get_err:
return ret;
}
+int reset_get_bulk_nodev(ofnode node, struct reset_ctl_bulk *bulk)
+{
+ int i, ret, err, count;
+
+ bulk->count = 0;
+
+ count = ofnode_count_phandle_with_args(node, "resets", "#reset-cells");
+ if (count < 1)
+ return count;
+
+ bulk->resets = kzalloc(count * sizeof(struct reset_ctl),
+ GFP_KERNEL);
+ if (!bulk->resets)
+ return -ENOMEM;
+
+ for (i = 0; i < count; i++) {
+ ret = reset_get_by_index_nodev(node, i, &bulk->resets[i]);
+ if (ret < 0)
+ goto bulk_get_err;
+
+ ++bulk->count;
+ }
+
+ return 0;
+
+bulk_get_err:
+ err = reset_release_all(bulk->resets, bulk->count);
+ if (err)
+ debug("%s: could release all resets\n",
+ __func__);
+
+ return ret;
+}
+
+
int reset_get_by_name(struct udevice *dev, const char *name,
struct reset_ctl *reset_ctl)
{
diff --git a/include/reset.h b/include/reset.h
index 4fac4e6a202..1e1e6ed127d 100644
--- a/include/reset.h
+++ b/include/reset.h
@@ -134,6 +134,8 @@ int reset_get_by_index_nodev(ofnode node, int index,
*/
int reset_get_bulk(struct udevice *dev, struct reset_ctl_bulk *bulk);
+int reset_get_bulk_nodev(ofnode node, struct reset_ctl_bulk *bulk);
+
/**
* reset_get_by_name - Get/request a reset signal by name.
*