diff options
author | Ye Li <ye.li@nxp.com> | 2020-05-09 03:17:26 -0700 |
---|---|---|
committer | Ye Li <ye.li@nxp.com> | 2020-05-11 03:22:46 -0700 |
commit | a2fd185001da15fed802de2b96d0420c3c18f749 (patch) | |
tree | d07c6930f371b713e7fff3fef807cf481b87f0f9 | |
parent | 31294b932df3ea21ef204a5077b4c8cef62cdd85 (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/Kconfig | 6 | ||||
-rw-r--r-- | drivers/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/reset/reset-dispmix.c | 290 | ||||
-rw-r--r-- | drivers/reset/reset-uclass.c | 37 | ||||
-rw-r--r-- | include/reset.h | 2 |
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. * |