summaryrefslogtreecommitdiff
path: root/drivers/phy/freescale
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-07-01 15:04:59 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-07-01 15:04:59 +0200
commitaa9083faa10f6fa42235eb924ca2d45ba95ed45d (patch)
tree9f9dc6888d00ca3a0885eb472df3a98fb5332a60 /drivers/phy/freescale
parent90fca074897a6a069408ee99c23f6897095b6d02 (diff)
parent5206026404190125436f81088eb3667076e56083 (diff)
downloadlinux-aa9083faa10f6fa42235eb924ca2d45ba95ed45d.tar.gz
linux-aa9083faa10f6fa42235eb924ca2d45ba95ed45d.tar.bz2
linux-aa9083faa10f6fa42235eb924ca2d45ba95ed45d.zip
Merge tag 'phy-for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy into usb-next
phy: for 5.3 *) Add a new PHY driver for Qualcomm PCIe2 PHY *) Add a new PHY driver for Mixel DPHY present in i.MX8 *) Fix Qualcomm QMP UFS PHY driver from incorrectly reporting that PHY enable failed *) Fix _BUG_ on Amlogic G12A USB3 + PCIE Combo PHY Driver due to calling a sleeping function from invalid context *) Fix WARN_ON dump on rcar-gen3-usb2 PHY driver caused due to imbalance powered flag *) Fix .cocci and sparse warnings Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com> * tag 'phy-for-5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy: phy: qcom-qmp: Raise qcom_qmp_phy_enable() polling delay phy: meson-g12a-usb3-pcie: disable locking for cr_regmap phy: Add driver for mixel mipi dphy found on NXP's i.MX8 SoCs dt-bindings: phy: Add documentation for mixel dphy dt-bindings: phy-pxa-usb: add bindings phy: renesas: rcar-gen3-usb2: fix imbalance powered flag phy: qcom-qmp: Drop useless msm8998_pciephy_cfg setting phy: qcom-qmp: Correct READY_STATUS poll break condition phy: ti: am654-serdes: Make serdes_am654_xlate() static phy: usb: phy-brcm-usb: Fix platform_no_drv_owner.cocci warnings phy: samsung: Use struct_size() in devm_kzalloc() phy: qcom: Add Qualcomm PCIe2 PHY driver dt-bindings: phy: Add binding for Qualcomm PCIe2 PHY
Diffstat (limited to 'drivers/phy/freescale')
-rw-r--r--drivers/phy/freescale/Kconfig10
-rw-r--r--drivers/phy/freescale/Makefile1
-rw-r--r--drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c497
3 files changed, 508 insertions, 0 deletions
diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig
index f435d6406943..320630ffe3cd 100644
--- a/drivers/phy/freescale/Kconfig
+++ b/drivers/phy/freescale/Kconfig
@@ -4,3 +4,13 @@ config PHY_FSL_IMX8MQ_USB
depends on OF && HAS_IOMEM
select GENERIC_PHY
default ARCH_MXC && ARM64
+
+config PHY_MIXEL_MIPI_DPHY
+ tristate "Mixel MIPI DSI PHY support"
+ depends on OF && HAS_IOMEM
+ select GENERIC_PHY
+ select GENERIC_PHY_MIPI_DPHY
+ select REGMAP_MMIO
+ help
+ Enable this to add support for the Mixel DSI PHY as found
+ on NXP's i.MX8 family of SOCs.
diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile
index a459a44f6ecd..1d02e3869b45 100644
--- a/drivers/phy/freescale/Makefile
+++ b/drivers/phy/freescale/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o
+obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o
diff --git a/drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c b/drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c
new file mode 100644
index 000000000000..9f2c1da14f5a
--- /dev/null
+++ b/drivers/phy/freescale/phy-fsl-imx8-mipi-dphy.c
@@ -0,0 +1,497 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2017,2018 NXP
+ * Copyright 2019 Purism SPC
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* DPHY registers */
+#define DPHY_PD_DPHY 0x00
+#define DPHY_M_PRG_HS_PREPARE 0x04
+#define DPHY_MC_PRG_HS_PREPARE 0x08
+#define DPHY_M_PRG_HS_ZERO 0x0c
+#define DPHY_MC_PRG_HS_ZERO 0x10
+#define DPHY_M_PRG_HS_TRAIL 0x14
+#define DPHY_MC_PRG_HS_TRAIL 0x18
+#define DPHY_PD_PLL 0x1c
+#define DPHY_TST 0x20
+#define DPHY_CN 0x24
+#define DPHY_CM 0x28
+#define DPHY_CO 0x2c
+#define DPHY_LOCK 0x30
+#define DPHY_LOCK_BYP 0x34
+#define DPHY_REG_BYPASS_PLL 0x4C
+
+#define MBPS(x) ((x) * 1000000)
+
+#define DATA_RATE_MAX_SPEED MBPS(1500)
+#define DATA_RATE_MIN_SPEED MBPS(80)
+
+#define PLL_LOCK_SLEEP 10
+#define PLL_LOCK_TIMEOUT 1000
+
+#define CN_BUF 0xcb7a89c0
+#define CO_BUF 0x63
+#define CM(x) ( \
+ ((x) < 32) ? 0xe0 | ((x) - 16) : \
+ ((x) < 64) ? 0xc0 | ((x) - 32) : \
+ ((x) < 128) ? 0x80 | ((x) - 64) : \
+ ((x) - 128))
+#define CN(x) (((x) == 1) ? 0x1f : (((CN_BUF) >> ((x) - 1)) & 0x1f))
+#define CO(x) ((CO_BUF) >> (8 - (x)) & 0x03)
+
+/* PHY power on is active low */
+#define PWR_ON 0
+#define PWR_OFF 1
+
+enum mixel_dphy_devtype {
+ MIXEL_IMX8MQ,
+};
+
+struct mixel_dphy_devdata {
+ u8 reg_tx_rcal;
+ u8 reg_auto_pd_en;
+ u8 reg_rxlprp;
+ u8 reg_rxcdrp;
+ u8 reg_rxhs_settle;
+};
+
+static const struct mixel_dphy_devdata mixel_dphy_devdata[] = {
+ [MIXEL_IMX8MQ] = {
+ .reg_tx_rcal = 0x38,
+ .reg_auto_pd_en = 0x3c,
+ .reg_rxlprp = 0x40,
+ .reg_rxcdrp = 0x44,
+ .reg_rxhs_settle = 0x48,
+ },
+};
+
+struct mixel_dphy_cfg {
+ /* DPHY PLL parameters */
+ u32 cm;
+ u32 cn;
+ u32 co;
+ /* DPHY register values */
+ u8 mc_prg_hs_prepare;
+ u8 m_prg_hs_prepare;
+ u8 mc_prg_hs_zero;
+ u8 m_prg_hs_zero;
+ u8 mc_prg_hs_trail;
+ u8 m_prg_hs_trail;
+ u8 rxhs_settle;
+};
+
+struct mixel_dphy_priv {
+ struct mixel_dphy_cfg cfg;
+ struct regmap *regmap;
+ struct clk *phy_ref_clk;
+ const struct mixel_dphy_devdata *devdata;
+};
+
+static const struct regmap_config mixel_dphy_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = DPHY_REG_BYPASS_PLL,
+ .name = "mipi-dphy",
+};
+
+static int phy_write(struct phy *phy, u32 value, unsigned int reg)
+{
+ struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
+ int ret;
+
+ ret = regmap_write(priv->regmap, reg, value);
+ if (ret < 0)
+ dev_err(&phy->dev, "Failed to write DPHY reg %d: %d\n", reg,
+ ret);
+ return ret;
+}
+
+/*
+ * Find a ratio close to the desired one using continued fraction
+ * approximation ending either at exact match or maximum allowed
+ * nominator, denominator.
+ */
+static void get_best_ratio(u32 *pnum, u32 *pdenom, u32 max_n, u32 max_d)
+{
+ u32 a = *pnum;
+ u32 b = *pdenom;
+ u32 c;
+ u32 n[] = {0, 1};
+ u32 d[] = {1, 0};
+ u32 whole;
+ unsigned int i = 1;
+
+ while (b) {
+ i ^= 1;
+ whole = a / b;
+ n[i] += (n[i ^ 1] * whole);
+ d[i] += (d[i ^ 1] * whole);
+ if ((n[i] > max_n) || (d[i] > max_d)) {
+ i ^= 1;
+ break;
+ }
+ c = a - (b * whole);
+ a = b;
+ b = c;
+ }
+ *pnum = n[i];
+ *pdenom = d[i];
+}
+
+static int mixel_dphy_config_from_opts(struct phy *phy,
+ struct phy_configure_opts_mipi_dphy *dphy_opts,
+ struct mixel_dphy_cfg *cfg)
+{
+ struct mixel_dphy_priv *priv = dev_get_drvdata(phy->dev.parent);
+ unsigned long ref_clk = clk_get_rate(priv->phy_ref_clk);
+ u32 lp_t, numerator, denominator;
+ unsigned long long tmp;
+ u32 n;
+ int i;
+
+ if (dphy_opts->hs_clk_rate > DATA_RATE_MAX_SPEED ||
+ dphy_opts->hs_clk_rate < DATA_RATE_MIN_SPEED)
+ return -EINVAL;
+
+ numerator = dphy_opts->hs_clk_rate;
+ denominator = ref_clk;
+ get_best_ratio(&numerator, &denominator, 255, 256);
+ if (!numerator || !denominator) {
+ dev_err(&phy->dev, "Invalid %d/%d for %ld/%ld\n",
+ numerator, denominator,
+ dphy_opts->hs_clk_rate, ref_clk);
+ return -EINVAL;
+ }
+
+ while ((numerator < 16) && (denominator <= 128)) {
+ numerator <<= 1;
+ denominator <<= 1;
+ }
+ /*
+ * CM ranges between 16 and 255
+ * CN ranges between 1 and 32
+ * CO is power of 2: 1, 2, 4, 8
+ */
+ i = __ffs(denominator);
+ if (i > 3)
+ i = 3;
+ cfg->cn = denominator >> i;
+ cfg->co = 1 << i;
+ cfg->cm = numerator;
+
+ if (cfg->cm < 16 || cfg->cm > 255 ||
+ cfg->cn < 1 || cfg->cn > 32 ||
+ cfg->co < 1 || cfg->co > 8) {
+ dev_err(&phy->dev, "Invalid CM/CN/CO values: %u/%u/%u\n",
+ cfg->cm, cfg->cn, cfg->co);
+ dev_err(&phy->dev, "for hs_clk/ref_clk=%ld/%ld ~ %d/%d\n",
+ dphy_opts->hs_clk_rate, ref_clk,
+ numerator, denominator);
+ return -EINVAL;
+ }
+
+ dev_dbg(&phy->dev, "hs_clk/ref_clk=%ld/%ld ~ %d/%d\n",
+ dphy_opts->hs_clk_rate, ref_clk, numerator, denominator);
+
+ /* LP clock period */
+ tmp = 1000000000000LL;
+ do_div(tmp, dphy_opts->lp_clk_rate); /* ps */
+ if (tmp > ULONG_MAX)
+ return -EINVAL;
+
+ lp_t = tmp;
+ dev_dbg(&phy->dev, "LP clock %lu, period: %u ps\n",
+ dphy_opts->lp_clk_rate, lp_t);
+
+ /* hs_prepare: in lp clock periods */
+ if (2 * dphy_opts->hs_prepare > 5 * lp_t) {
+ dev_err(&phy->dev,
+ "hs_prepare (%u) > 2.5 * lp clock period (%u)\n",
+ dphy_opts->hs_prepare, lp_t);
+ return -EINVAL;
+ }
+ /* 00: lp_t, 01: 1.5 * lp_t, 10: 2 * lp_t, 11: 2.5 * lp_t */
+ if (dphy_opts->hs_prepare < lp_t) {
+ n = 0;
+ } else {
+ tmp = 2 * (dphy_opts->hs_prepare - lp_t);
+ do_div(tmp, lp_t);
+ n = tmp;
+ }
+ cfg->m_prg_hs_prepare = n;
+
+ /* clk_prepare: in lp clock periods */
+ if (2 * dphy_opts->clk_prepare > 3 * lp_t) {
+ dev_err(&phy->dev,
+ "clk_prepare (%u) > 1.5 * lp clock period (%u)\n",
+ dphy_opts->clk_prepare, lp_t);
+ return -EINVAL;
+ }
+ /* 00: lp_t, 01: 1.5 * lp_t */
+ cfg->mc_prg_hs_prepare = dphy_opts->clk_prepare > lp_t ? 1 : 0;
+
+ /* hs_zero: formula from NXP BSP */
+ n = (144 * (dphy_opts->hs_clk_rate / 1000000) - 47500) / 10000;
+ cfg->m_prg_hs_zero = n < 1 ? 1 : n;
+
+ /* clk_zero: formula from NXP BSP */
+ n = (34 * (dphy_opts->hs_clk_rate / 1000000) - 2500) / 1000;
+ cfg->mc_prg_hs_zero = n < 1 ? 1 : n;
+
+ /* clk_trail, hs_trail: formula from NXP BSP */
+ n = (103 * (dphy_opts->hs_clk_rate / 1000000) + 10000) / 10000;
+ if (n > 15)
+ n = 15;
+ if (n < 1)
+ n = 1;
+ cfg->m_prg_hs_trail = n;
+ cfg->mc_prg_hs_trail = n;
+
+ /* rxhs_settle: formula from NXP BSP */
+ if (dphy_opts->hs_clk_rate < MBPS(80))
+ cfg->rxhs_settle = 0x0d;
+ else if (dphy_opts->hs_clk_rate < MBPS(90))
+ cfg->rxhs_settle = 0x0c;
+ else if (dphy_opts->hs_clk_rate < MBPS(125))
+ cfg->rxhs_settle = 0x0b;
+ else if (dphy_opts->hs_clk_rate < MBPS(150))
+ cfg->rxhs_settle = 0x0a;
+ else if (dphy_opts->hs_clk_rate < MBPS(225))
+ cfg->rxhs_settle = 0x09;
+ else if (dphy_opts->hs_clk_rate < MBPS(500))
+ cfg->rxhs_settle = 0x08;
+ else
+ cfg->rxhs_settle = 0x07;
+
+ dev_dbg(&phy->dev, "phy_config: %u %u %u %u %u %u %u\n",
+ cfg->m_prg_hs_prepare, cfg->mc_prg_hs_prepare,
+ cfg->m_prg_hs_zero, cfg->mc_prg_hs_zero,
+ cfg->m_prg_hs_trail, cfg->mc_prg_hs_trail,
+ cfg->rxhs_settle);
+
+ return 0;
+}
+
+static void mixel_phy_set_hs_timings(struct phy *phy)
+{
+ struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
+
+ phy_write(phy, priv->cfg.m_prg_hs_prepare, DPHY_M_PRG_HS_PREPARE);
+ phy_write(phy, priv->cfg.mc_prg_hs_prepare, DPHY_MC_PRG_HS_PREPARE);
+ phy_write(phy, priv->cfg.m_prg_hs_zero, DPHY_M_PRG_HS_ZERO);
+ phy_write(phy, priv->cfg.mc_prg_hs_zero, DPHY_MC_PRG_HS_ZERO);
+ phy_write(phy, priv->cfg.m_prg_hs_trail, DPHY_M_PRG_HS_TRAIL);
+ phy_write(phy, priv->cfg.mc_prg_hs_trail, DPHY_MC_PRG_HS_TRAIL);
+ phy_write(phy, priv->cfg.rxhs_settle, priv->devdata->reg_rxhs_settle);
+}
+
+static int mixel_dphy_set_pll_params(struct phy *phy)
+{
+ struct mixel_dphy_priv *priv = dev_get_drvdata(phy->dev.parent);
+
+ if (priv->cfg.cm < 16 || priv->cfg.cm > 255 ||
+ priv->cfg.cn < 1 || priv->cfg.cn > 32 ||
+ priv->cfg.co < 1 || priv->cfg.co > 8) {
+ dev_err(&phy->dev, "Invalid CM/CN/CO values! (%u/%u/%u)\n",
+ priv->cfg.cm, priv->cfg.cn, priv->cfg.co);
+ return -EINVAL;
+ }
+ dev_dbg(&phy->dev, "Using CM:%u CN:%u CO:%u\n",
+ priv->cfg.cm, priv->cfg.cn, priv->cfg.co);
+ phy_write(phy, CM(priv->cfg.cm), DPHY_CM);
+ phy_write(phy, CN(priv->cfg.cn), DPHY_CN);
+ phy_write(phy, CO(priv->cfg.co), DPHY_CO);
+ return 0;
+}
+
+static int mixel_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+ struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
+ struct mixel_dphy_cfg cfg = { 0 };
+ int ret;
+
+ ret = mixel_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
+ if (ret)
+ return ret;
+
+ /* Update the configuration */
+ memcpy(&priv->cfg, &cfg, sizeof(struct mixel_dphy_cfg));
+
+ phy_write(phy, 0x00, DPHY_LOCK_BYP);
+ phy_write(phy, 0x01, priv->devdata->reg_tx_rcal);
+ phy_write(phy, 0x00, priv->devdata->reg_auto_pd_en);
+ phy_write(phy, 0x02, priv->devdata->reg_rxlprp);
+ phy_write(phy, 0x02, priv->devdata->reg_rxcdrp);
+ phy_write(phy, 0x25, DPHY_TST);
+
+ mixel_phy_set_hs_timings(phy);
+ ret = mixel_dphy_set_pll_params(phy);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int mixel_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
+ union phy_configure_opts *opts)
+{
+ struct mixel_dphy_cfg cfg = { 0 };
+
+ if (mode != PHY_MODE_MIPI_DPHY)
+ return -EINVAL;
+
+ return mixel_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
+}
+
+static int mixel_dphy_init(struct phy *phy)
+{
+ phy_write(phy, PWR_OFF, DPHY_PD_PLL);
+ phy_write(phy, PWR_OFF, DPHY_PD_DPHY);
+
+ return 0;
+}
+
+static int mixel_dphy_exit(struct phy *phy)
+{
+ phy_write(phy, 0, DPHY_CM);
+ phy_write(phy, 0, DPHY_CN);
+ phy_write(phy, 0, DPHY_CO);
+
+ return 0;
+}
+
+static int mixel_dphy_power_on(struct phy *phy)
+{
+ struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
+ u32 locked;
+ int ret;
+
+ ret = clk_prepare_enable(priv->phy_ref_clk);
+ if (ret < 0)
+ return ret;
+
+ phy_write(phy, PWR_ON, DPHY_PD_PLL);
+ ret = regmap_read_poll_timeout(priv->regmap, DPHY_LOCK, locked,
+ locked, PLL_LOCK_SLEEP,
+ PLL_LOCK_TIMEOUT);
+ if (ret < 0) {
+ dev_err(&phy->dev, "Could not get DPHY lock (%d)!\n", ret);
+ goto clock_disable;
+ }
+ phy_write(phy, PWR_ON, DPHY_PD_DPHY);
+
+ return 0;
+clock_disable:
+ clk_disable_unprepare(priv->phy_ref_clk);
+ return ret;
+}
+
+static int mixel_dphy_power_off(struct phy *phy)
+{
+ struct mixel_dphy_priv *priv = phy_get_drvdata(phy);
+
+ phy_write(phy, PWR_OFF, DPHY_PD_PLL);
+ phy_write(phy, PWR_OFF, DPHY_PD_DPHY);
+
+ clk_disable_unprepare(priv->phy_ref_clk);
+
+ return 0;
+}
+
+static const struct phy_ops mixel_dphy_phy_ops = {
+ .init = mixel_dphy_init,
+ .exit = mixel_dphy_exit,
+ .power_on = mixel_dphy_power_on,
+ .power_off = mixel_dphy_power_off,
+ .configure = mixel_dphy_configure,
+ .validate = mixel_dphy_validate,
+ .owner = THIS_MODULE,
+};
+
+static const struct of_device_id mixel_dphy_of_match[] = {
+ { .compatible = "fsl,imx8mq-mipi-dphy",
+ .data = &mixel_dphy_devdata[MIXEL_IMX8MQ] },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mixel_dphy_of_match);
+
+static int mixel_dphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct phy_provider *phy_provider;
+ struct mixel_dphy_priv *priv;
+ struct resource *res;
+ struct phy *phy;
+ void __iomem *base;
+
+ if (!np)
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->devdata = of_device_get_match_data(&pdev->dev);
+ if (!priv->devdata)
+ return -EINVAL;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &mixel_dphy_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ dev_err(dev, "Couldn't create the DPHY regmap\n");
+ return PTR_ERR(priv->regmap);
+ }
+
+ priv->phy_ref_clk = devm_clk_get(&pdev->dev, "phy_ref");
+ if (IS_ERR(priv->phy_ref_clk)) {
+ dev_err(dev, "No phy_ref clock found\n");
+ return PTR_ERR(priv->phy_ref_clk);
+ }
+ dev_dbg(dev, "phy_ref clock rate: %lu\n",
+ clk_get_rate(priv->phy_ref_clk));
+
+ dev_set_drvdata(dev, priv);
+
+ phy = devm_phy_create(dev, np, &mixel_dphy_phy_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "Failed to create phy %ld\n", PTR_ERR(phy));
+ return PTR_ERR(phy);
+ }
+ phy_set_drvdata(phy, priv);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver mixel_dphy_driver = {
+ .probe = mixel_dphy_probe,
+ .driver = {
+ .name = "mixel-mipi-dphy",
+ .of_match_table = mixel_dphy_of_match,
+ }
+};
+module_platform_driver(mixel_dphy_driver);
+
+MODULE_AUTHOR("NXP Semiconductor");
+MODULE_DESCRIPTION("Mixel MIPI-DSI PHY driver");
+MODULE_LICENSE("GPL");
I;7 :t7Ik,򘽷s]RrIAs>,ɴ+Z]q=vs(w{v8dSak !KqW*Sj\Qն5UW:*c2F6^$zu5d%FՔ :=7ǥzo~O0A?ɖ,9fl 1D3/wNj2"W!b76M˲ͳ'1-@$qgSSv#!,}L5/@`ot.ty>HqwvR\ڍ!{P}~;5f4U\ذGjX/_8qPkRACض{<L)ZݦڛȫkHR햆'JOB3oR[vpx'򓽥y"eb3,yojLl"OVɣ)J4n1!=5BPnx#}ڹq$!=a&t].l>2W58ecy-~ ۙS㵸0/`IAշ^>iFr5HB'} Öc3}19\ØI9hq ߺ!`'.dD< iB"ֶ}$}lSBq;{LL@%#6s@knޙ ѾaN*w e7[(}Al hߓmT9grS Бz5/i,$I-w E-7:3aNmn{MFwXTߺ<^iPN&tЇl[窫mt.LíuĂAM <3NZf;+CgU&+`s.1^:FǕPIncԳ[T͛t}^b4s Y Ɓ?Czgd:پAE ; ;V>VjmR]#UUT OHэiuM3I5T`0,$SGy϶ #Ҏl)% hB?md4TNP Z`Q ! wF 52-z1zw~AT}/z\dž'v,?U:f_Ԥ:œiq\df=W{'>b.Qj0@K{p0$𩋠bod%?ܜu`MfgFK*$N**Nii gˡGbЇ~E4eS)7)iӍ|V,@Y I, |Q=qh/Qur$HJuOu^6*fפȦIbƜ*`L$> |.)_͔.UC ^̃>\ `Zd"ZX^H~5>t04h/؝u=Lz! >.Q 4;bQJ^v{8Խܙ?tCgKĂ}Vf=ډ ts/7]4lo,]`B$-$bQ- n] l'QSd9#V` kGG n9a`ZZБLk-G2O`615fkU:gXn7j |5쁡CR4OxY(ȡDaґɊ^ so8p]H㕀M‰%[rs2+ت}dHƻ+ ͍BBOfICAq0f*r{/jͤJXd#B]XvB"zC2vvk[/2 Y.00LDg5 TS F\]9yF ^1)|1):^Cߓ}Eׇ7-7Yfѫv{DFIZfZAscX/jO S8Yhۭ4OG cN(Tҭd#.OfϞv ɡ FJC*KQOyP+jG3%!LWiqLs?z]n2}ңr&:;~7Ri Tό,66{||.V,9$ǭS:]!ꬓǕ22ߐ`$eN;WjmAyt]ӱ7oMzk7sIlj R{4K"G5\zzfZ\*9,\Cv ^LF^f -YZ=%g6򜼿p[!ORGN'W^<^6B91:Ĺ;j|W "y){Kx(zEW٥A$ލw&?>>O˹"w&5.s\=b(t"'n 99pjk].\)i>B/8\||#LpZ\wbm2&76cgE[vj:Mv*H.7"Q Q͑jTYUɸ‛K2zQ)tP47Q%ccz %-X7aE#w ]fJɯUgJh* |j |+QCWVefSWlW} IeޑK*3Dv搜\~iS2[Hogg$i%Za?$r&Q%$D>̤ <{;=,v'FaMEtiT,@ 섴L}A\cۮ6:@O9 | B=yzmhW+g<yfUC $\G@uOO.*aB~ǹ2`p+)vхla`!ZDYYbwz KyhK~p0pLkʿDzwdJ'xGۧ"aasf`( N)}hY;yy7%?ЎwԒW:$*h g<잡̄>7)'7wӄÅJ5Ki1](I43v<ԑb!*R|kX!꒶']2O=ʷJr_'G+(K(T#y2ߒDNff$e+T>`d5h/)nb/rI/ow=]9Cܐ:U48ʕj-Zrښ#Y!N]ˉ6!w>~?߬}Q̘v"Ǹ/W*&vT?p/iF+Uju4T#Š:]LcлK͏Jm9]IK}o'MI]ct:4k \NЯvVwM w֚a0!m_`wΐ\ɱx]aၗS3=1L,G} `P;0L2Nu ͐9ktE[num]0dqt(ϟⲵEB; O7o_G-ޥv3ٿޛZ,PҬPӠ=iZ5ɸL,!Z{KHQ2݃@h}R3L ^U@A:,\&`uj5s9LJ0uDFIg"hGH7|kw-&K輍N$i7]vCf giNJW[l9 ʿIɏ"tY= q?lCT\h=r2OM_s< 6S'_ai ,FoRS8̔kU3L}3Hdf];0Mu]UL2=V*RZmѩe7Ka5#i=ʖx,e%LU\oN%cUZ5(+WQWR>jLݑv&+[j;4D GK`9ܺ&ޭIDYAgf; 8o2_LH@8\%+_@LAJaHo# 8H>ɱDK9'wp!!U* DvYxS'MQ)+ah4[ti5` \ r2n VP۝#7 `EOaa}\OL׻ P+׻Dkv8@x/k`Eqdva7%+?wt 0af՞-- {A6'!AʁlrԍhZDxQױx]}KX3SciLó?35ULpdP:E<7.ξTl Ә@5?L>,mѧ.L^d\/1rr/'}ICAXf[79u"k`ɂgX$kltz_@@^{t1b2{cYܴZХ ! Pe:LP E^r95 I&]TUpdU#< `a7!7M5c7ᰐ?uW`,[̙$iÛ55FHN &"@&/fLS_;>fn9>tQ=ZtvJ&W5.\G0?E" y*-"pG\7-2)*{O^hgKSb*9e索U#5s"~O2MfLv1ڼ7}"q~I+yf!n~=eM)޴#"%W`UJg̫rPk(JxG$-s'QUT_SKsn x+Q,3ē,nnx&UǽN@ի@աUW1,kTh3̪)BUMЈ8 y3rS%"h l.Lc'z7}1k&~ﱏmSԀ/0a>sۋ2=70xa&zD HGÁ|/fL$> 'oיceDvӠYRIQ &eDaBx.EyިQoW[ЧfvgLר9P Ch7Tq/p{{z#KvR?ɀG\R8|7CJ $FN<3oK%w$4)}s;É6yTe`Y2TvzC]JWw&6'^o4`<?BoZ|zs)ezo<蔹Bpsd)z58ҽV}u5RhWƮ4HV:uȲ|r?tZPf8v}2}8L$2qϓL61Vl>4 &BUJޗ\D\g"gFUZR]kkMf σ[xQs)uXNZq`Ie#:˚.h.ٛV0Zƴ5wĮ1dt g.)$\kH8`.l4LQpT&L!Pe!8 峦M'F2.G@XlPpqw VY5k>pN6#i|.d[I.Σr| /Dq`CM{Ck`1֮D! {.pKoFXǣVoWjAGU AU G"cqTo*p Z2 >ra:82-{Wf|}/} v{rp=N:!i<$hĪ6V/e2ݠvQOC߀*{I{Wh"lgX|DVGhKf^OPC}߼=i-ɭ/z$SDZ(`)biRgø8\')qa] +IǛeH92@&'<_I+uջ 4j6ʚ]MD^tLo" ltN^CSb ^>9q%Gyc9iMIR%|)V*TT>9o]XY@7. {NDne]I8U(GnKMR$%PtK"u)mVN#!H1%W6k&f3yP>mWHEti4D GrSa+R*@`yk5D3rO2FknZ=˙pekNGI³^ijT٫eVWFC5(f 哣2Vɐ\N*ըհB6G-Unjc, Scp(Hm` I#"Sft2\ ^g4F$a+nRSm4ˆeA } ^զq\o{cҧe[42tFsaξρSr!N2̀LsF/3~}ٯF tý}XXO2!O U0{5#)& u0rbAw&s9'5Y`o @.!9 $~mQZ}2aYa.h(a-3&rc@K~^:wŬT: (&n1/dJ=lێG4䢪RjtgvCaa[~HnԬ19?.Ït5l]$.~`FM#F|ib yJ%.V]k$1%=3} 9 KvORzbA *•d6-&b0VkJ af->: _U6$P5v (9fNӠ`Ymڽ4*CUkV'!]tdd75hW=3NdsB'?J{23Eqq˸?tNlic7?/~`6dn_d#\ƶ>GNvT-iᰯ 2<%zϜLbY;R*/_d_q CN(M,G\pV>-Sr&zT$)^<0xtהTjn r!gzwnǠLhJsJ~ݥߞ g5͚5[ hfk,=ZU jÞ MIUB`:`:3%h\l[F5vԩ jVVj߿W~aі!nqM\R \9vFnv0w# Rw~ݻ>y jZۼ%zalmY$s:X 6Ծ:dNG#k뿊+nE#?ȚQB 1 #K_WJbR1[3sI#耖Y,bl|c~졍ɡh9n(Y㼬 ,$=P[iDooE ']h6'"ca/aXʦ+'Sr L8Y=KvfjB#b^!!@P bZziJG$Rٶ 9}szg Ch=i<3!+L.5[ی  \V)F>~~<g܃HkfXBbu92"3t?KQAT}!S(ɝ: P8m`> %yre[UYx8sD$լwo$B䣺|iҨZE 9w}fL52 4c|s=F.-IbE8F@n3h瑰gc3FWZcoޫL;3vh3Ч=yɥH^]Ƶnsuڎ2L@шba;&RL㰃zk#Ty/ӧV];7MHa>IWS SNDzok%E("ͱ{ގJb:˿􊙤s}|HȡAPBw yA-y}D= /˲辅YӉ6?R. AQrK,7Ov=~H0_c/7JfNN]O>++>Ûtg޸M[oqv5Arhn?*0qw=QԷoSfJ ;u"e)|Ȯr@G@>k\4Ed0&]J%: !Zz [-UwƦ_iDsVʪC 1~CuK6I/xK.r:IgF5ӦG\+#U SSC-8{;~GZY c?T(7zFz]!--/0M!UI^owtmE ^A>>g9jIft9ՏlM ?wsvC5l,֌=:Dr&o{7(Z~ތ%Q: vuP`Xۯ,.Zb`'HioqBmr89 6N'\'ɯi4s!{C&{x6+Cytq>T7}Y\nh#"2P;9I!]DʜN|f=(wKq$P.Fʟht&t%dj;ٯ%%UjVCQoSs@Eeub/5KFW!QR[d#Ei Qv䢅>kSf=`=3/}-M0ҙL} l*zo/{i{3\Ty$i+)zw'Çd0N0Pc nEYÝ JQgZKD `a;k=yG^AiHie'L'^tW<^ \2j>yFޠ Kz }} 4H6aNyk!i&K( vϳeK̑0;qkĈ=tdz9{gy\R19 Y~?yFii]hB}2 nEWaKg*{%j-w-9ٵ~!Z=!quE/C9gsId]/o~HTMISJQWvG]*S69I"Ὰ'_ڮ*%YTyރ!QaiIXU qAڭv9WwE`!|Г߃'+,70sD$`^nDF)~VUuoJ_̨~BmO[j2ilq5 lw ?pO3Ii2iQY-^)Ӏ5#d ca[ۤ)N] SX#,P3>c@oP8 hI cMm bPT)V`g(ՌxK 3:95}#Z4(n&TR\C[0 d/kzr}JjӘ9/H4C'[";wOqdK3=3דiCNPܞFRh:wpnHeީJ]bGp[xG_UۘP5[:0d?_F)rv4C5.Mc5e5Rؚ MgP+{M*kd(}..rb^R&ā(Ə3ABu 'XXr0K΃X([Gņp==xӴˈԧHKb q13Lwe]l*Q]E54t,"1SСRH6mz:0W[|}5nJ}0,t,?2ig]ci#[2Ϙ]օs6:2&ouu*-3`䮸IH,{WEp(ǃS9z&p3 ./KM>SV!t8g7r;sڣp`O*#h36s.N`A,@'!pL(ItpyEwn48-D&[R,QRٷ0sKC]Ip9m(L9΍q}};./3] erA蔬X#o)smu_YfҐjؐr!3$WvX狠%h%4酓n(@~󂱣)Dyp?EpN㌸+aFi@jt fHPt֜',qh?|{GnRNvZ̤w,Xs1&O"e;;%? uY7WЃ*'y lk c GsO)sa|Y:_\f,LE+2f5`P؁M"u>i[|4gYWncXڼqˡv@h2&"a|'>QvayZQhS.KDpR)YdYeNeF"Idq;7P+}eQz[M)-/K.7 FdE^ϡ?ӝeXd\bV'a^ d$jو$jKP%]o'@x0q˱=6[kW|{u?E\R4(fmDNn A 4@gyHʶ,;$}:uj} o[UGEi`ˆs}BVT: /?Rʬ d_LH|$Rp)"p8nͳԟـZlkJ$O~4wN:ݦ\rcd.UkQlKadE6'}fBųvs?^^}0VWYt2c -|+唑:klcP?kwK;]N% RjqިA0:zˣ!kWcTX-v[9lTky)\@* `>,絈tG< ֈa/ zIoȲ%u)/-d{_D|uPV541ɸTzyX%p&0Q\a=,EoPd/ @]g]/S!-=cjT-RB*k/4>/uNK2K&`8"Gi;\ S6K ߟLgX? Wl^riʋ0 AjaIs/R>4-f9E[8J-C+u:O\UX$v ]bI"Ev;Jh;GYi`#:m"21;S{aXN>y-.J1~hk 5ܪ'CD9K?3u/bNtpin$V6] m1 >6[>Wԑ]$#WgOa5 K =]3)F6g ^P-x2wm)f-Zf"_X%NJn: [ICeÅWj~0$?9=JiR4 bV7 bzGP}vZPӰ""[,'1֐y4"4lMI'86;v3~Zog׾7m|˲7(@X TY#e!8k u~|n?(E%3׼k>swVΗpXQZe#[M(>D*J/:ilᄸ/1ɜÔ݅Acf[ɖ!`E|IrU\҅o#rȚ|NFw\8RY{癗e8 g.JK*&7>G_Уf9SU`"h]f 5 }$Kժ}ڬLCe!mD}P,,%9+VN\2֯% Wi*v(pN4a̓Xu 2aqa-ӸɆs=׹ۻ_ ģtTP}jsH)8c:I6<`ZK.k_#~ė Ws -/gȺT`T+Vt.&w"c'}YКE;u5$4 '; S.FɆlL1$ C+&~:V{M Kn3B߱wPr;M{كr.l$M9aرE.MUaj 2e&Aj1vک.sJiGnj͐U9CSGv]n.z1r"kͭJO7vnnGCIpdSUd U.u(G:&qP82+9$^_\rnKYcfA8]L˓ň5W Ӄ"+Hn2+&F\ k* ^@ؗ >n׃$. L1mmNB(3G5-3NPllV<(@-m5b(Xz6 yWwhS MڮGd)BS=XL`:.|Cs\mV:{fҙ¼.¢idSas!W>!NwUr>YA9vx>Cy9vY%6 &Kcy8k+C$ /HRR r+{iʪDbw 7#zRtQIDc? ujʼ/ќY9Ne,9) gӘߪp-$YDWu2]ـcNxx~H*hܤ_ϰܻ{3|>nl߾m?&^楍s:oϾ,ul4O3F$!&; dZ' fw+A5 Yvq{$IތTs0A;u(DfX Vp8'[vvgpƢڮeJobLdD;]%^T4mvD~KOw~o}79wW_쿟?iu9צ~}oYB5PV@Ah/ }?w=glЪ sԋt ٩MTI>,C LP p:ˍC$0Ag@pkql2+|9؛AI)Æ vS0[H1~ j;T*?;5}/gd/,eX&ΊW{_잻[~peV+Tvׯ ĉH4ҩ-jf1j˞~]`O ~; W<w-> 91SxdF.t(틶BoӖ RS5tt!cl(Կu[$44d~V; m x6*MZׅp:&;SV\r5Dd(?ݢ*]w;wοZ"ScTy4Ә`L f{pG$g4?GǯLJV>}qtU"^y D5Ld{i[F8:' hvWre7m|SQ5N8 V ߙ q1 {,MUC$XB^ӭW֎!H]¦3ȣo/fr>M8A_LS&N22Y)/:Yt] ++iS_|?ϧ#"|?Ol3Vڸk͛嚿-udLH[ڞzFQFn:r{_g.2םDZتZ典,\il aO] ?kX9i```7$hO1țZW@vF)SďӸ ,VcSƯ*{CIFcP|ē0ADMe,DZi.Ox^Zן{wۻ慄?_ο!WvokxݕTż:+.rVb2wD_6GCP*qMI{| \ UiV:gm|hw&Q@LJ."s\OotA%TN YtR@kY53r #&QwK ߖ`-Z i`8Q]y UC t LЃFh򮛪MIK9*1yOUA%*y1 45Th*U|Ro>Zu$JщffǢ--,;:R ]K'ӓ?=iVmF轪yQ*3+r*~eař3uŠ=-jD3I)͇_ߋ-Fӽquz٠;{]|~9pCe&^! /=NdU眐V갹?'`v 9%(=.&L֦re]>\_(iDzpLմ THCXcָx>1޼~dUs.‡b9)2Cp/8:׌}ƢnW٫ Qq2Qi}Ͽ:kr@cVG90evxdI(?7NE ~e1A虌[-yfZ6"ᔗnz>s~d(õ &ϼHi|NԕWڻ *nP=XYyCutNr6 [j&B8A*~V9\``׏7lo-k ї;[c!byͿ BޏY}^pspyC{ rr-)wzMԶ' \zQq!8I&f'(ge+XVB3s6JXm .ߓ*}6"e,x#|X`w{seW"u4W IUJ$G8 /ݾ;躆[[q(iXA#Q J[V%f ?@!>;D5:/MH^䨈a<]2ǏR}]Bt٩\=Y7L'f,Ko`ʏU朩zNW#O*[2T{a 5֯I#O٣/$9铧EJ 1 9{`\4J'( #Y)loӰ?W8"KgV}_\)N|Z?,{K${R!~k_ݯiV=vQ|g7ܽww/_'>\ a -2-6э&? ހ1 hP$+QK4}4}^)7czb^壡Hr^1h{bd3m@IUyr\ޜWKgp@8vCڔޚ[ Kw:ۛ@`vv;;X2dZfU>aS5R>=E;Oeē+(H^ ~R zr}MvfG#- O+z';]FBr*p㨶0Cu69NJ^Oڦ 5֝~'mgQeCYSϲIem)-vLqy`ƾV:}ޛuާW<9@ʄfϹ/'^ DH</bB!8 A#v~7W];wϧ%A]ypKgޚL[cj-ӗpݓ *zog_yh"o}׶&d4m)P@lit`LY#puf.5#4d`B//9&0ik+g*[;~U6brZ**(o܈ [Co8JTJIJbfmw7"3^RH2.-K"{{MDRPIQ>Cu $+@}QS1]VSP_ukq~E -!GQEc4!-fCf+-tv .! uvge<W WϏꌺ_x%XҊTICŻ:pg#/&?wϟs"N ow_~tl~_;FϾl{ҽT*}r?eG伮T^'6Lw5DcY-dXNH% Rn4޽L_*6lg꣼!P#?v[X-_$<@Ebq" gwg/ 7ϙY?3ݼXi>q~-b4QnrTT@ԭ|r8>BWϭ\UJVEm8I)Cr,3gwҶ2 G>+]b:i@m\ }X;|A@ү*+t@c |M0+|Uܷoxޅc,[9́ cعSo@"/NNYGWsSԙHQX?> ~>W r7;vWܿE?5?6o1u57Ȗ8y B{K {w ᄈw߽w߻w_ûyny7y{C*l6uZ'+k Ɩ -YFY:A=[AWmt -۲%Lh^bNǜ3@ϲ O]#D".Ѣw}s>x{SaVT /yʋӜZp[t:ov3% Oa3^9XҼeo2rmu?reR ^|F+ݧZR#dO) Qѿ&D^5_qZ5膖2,Zy{c8Kn{K~o}O--vZBA۽wފs Y##Ny$YB9?䝢7#8W_䳎wI EѮe(7 Ƃ,NE9:糢e&EӸrúEL&U aMZh1?WaZ91' R_^j*bwר:-~ #[4Ve%(E`h(O&EM P$\΀ i*$d+vO]i1fi9˭7_P[`ͤ0\³"ڋr<.'P'$c:Uٌu70O F~Soo_7_} ?ˌF6vO7hO~{quUpyE/ !ʃ!ܾd}\ZgBSTuoF[ouw;;jErE]$^\Ir?Dg#>0#E2j'Z=7~.dˋ a7s5+}V!?+P>ox]2yxe ~|<_sua p7:{<@P=8j]NKVac8 Vhq#i_CtP_`dxMR__E.kryI%j.wG< kԶm XY6(E k)KRvNuiqnU^^i$@]k.f5T z{j6(: "1Z[4XXH@zйHD`p-{@:-GTVL"<%Ճ=T"ލEC9+n{|<'kJFs R,c|"SI6 2-qdKMaQ| =lܰ #"Q˂Fp\u'o{_.V+ʋYWT=[Nܠ|Pz?Qrk1K}cոs}²Hv,Fnnru6Vb5~E@e1QPFGbvjS˾̧!f#OB)ZbťdұIMc-`L4q45Z4XszVż xH:6pt֞,_Lp܎i+&al&$A2nPMgeff)S2y;TL헶a5U1ʇ ; 'ܐ&`͎<6^goP/"4%םwc"DDВ zΑj<2%y]-vCos{ϭ [*C[Bmlӽ P?Jnμn}6"7<=E:m8d)&7"#pʒ 2#t\%T9} ˄bʟyLNo iK-|uXt7 ^cjPBY50/fc{?i?ǚə`\S9q a*TM$kG+\Iy@]QV*7If6=R$.pjit8WJYR *i' ;~9vRh%L@niʨTptv  JKs:wg\Fń$G՞ۼNYUɶ4@޼yk~pJd&0^*_^YrF_&7a98p5n]Zup ޶C^2@XE+f<.yw\nxb$O#㴝,ևlrGx)lG vy}hPJ3]3ϟ_"tBYX1Vs]m/rBt1M, ty/ mwᒮEqsoo}u|k_~~eK !kZR6ϚK=^A2׌z\)b)A|T0[j0e4NoN't2{By