phy: Move Allwinner A31 D-PHY driver to drivers/phy/
authorMaxime Ripard <maxime.ripard@bootlin.com>
Mon, 21 Jan 2019 15:45:50 +0000 (16:45 +0100)
committerMaxime Ripard <maxime.ripard@bootlin.com>
Thu, 7 Feb 2019 08:34:49 +0000 (09:34 +0100)
Now that our MIPI D-PHY driver has been converted to the phy framework,
let's move it into the drivers/phy directory.

Reviewed-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
Link: https://patchwork.freedesktop.org/patch/msgid/2447609da5b80f148c79b2b2a263a0e779f3e82f.1548085432.git-series.maxime.ripard@bootlin.com
drivers/gpu/drm/sun4i/Kconfig
drivers/gpu/drm/sun4i/Makefile
drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c [deleted file]
drivers/phy/allwinner/Kconfig
drivers/phy/allwinner/Makefile
drivers/phy/allwinner/phy-sun6i-mipi-dphy.c [new file with mode: 0644]

index 2b8db82c4bab16737234076c32d6ec3f454df4a7..1dbbc3a1b7636f87521d89a7843ffa64fac09eef 100644 (file)
@@ -45,20 +45,12 @@ config DRM_SUN6I_DSI
        default MACH_SUN8I
        select CRC_CCITT
        select DRM_MIPI_DSI
-       select DRM_SUN6I_DPHY
+       select PHY_SUN6I_MIPI_DPHY
        help
          Choose this option if you want have an Allwinner SoC with
          MIPI-DSI support. If M is selected the module will be called
          sun6i_mipi_dsi.
 
-config DRM_SUN6I_DPHY
-       tristate "Allwinner A31 MIPI D-PHY Support"
-       select GENERIC_PHY_MIPI_DPHY
-       help
-         Choose this option if you have an Allwinner SoC with
-         MIPI-DSI support. If M is selected, the module will be
-         called sun6i_mipi_dphy.
-
 config DRM_SUN8I_DW_HDMI
        tristate "Support for Allwinner version of DesignWare HDMI"
        depends on DRM_SUN4I
index 1e2320d824b541420bdfa8f6f17c50eed59b121c..0d04f2447b01caf08de771643d2536239c62654c 100644 (file)
@@ -34,7 +34,6 @@ ifdef CONFIG_DRM_SUN4I_BACKEND
 obj-$(CONFIG_DRM_SUN4I)                += sun4i-frontend.o
 endif
 obj-$(CONFIG_DRM_SUN4I_HDMI)   += sun4i-drm-hdmi.o
-obj-$(CONFIG_DRM_SUN6I_DPHY)   += sun6i_mipi_dphy.o
 obj-$(CONFIG_DRM_SUN6I_DSI)    += sun6i_mipi_dsi.o
 obj-$(CONFIG_DRM_SUN8I_DW_HDMI)        += sun8i-drm-hdmi.o
 obj-$(CONFIG_DRM_SUN8I_MIXER)  += sun8i-mixer.o
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
deleted file mode 100644 (file)
index 79c8af5..0000000
+++ /dev/null
@@ -1,318 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (c) 2016 Allwinnertech Co., Ltd.
- * Copyright (C) 2017-2018 Bootlin
- *
- * Maxime Ripard <maxime.ripard@free-electrons.com>
- */
-
-#include <linux/bitops.h>
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-#include <linux/reset.h>
-
-#include <linux/phy/phy.h>
-#include <linux/phy/phy-mipi-dphy.h>
-
-#define SUN6I_DPHY_GCTL_REG            0x00
-#define SUN6I_DPHY_GCTL_LANE_NUM(n)            ((((n) - 1) & 3) << 4)
-#define SUN6I_DPHY_GCTL_EN                     BIT(0)
-
-#define SUN6I_DPHY_TX_CTL_REG          0x04
-#define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT       BIT(28)
-
-#define SUN6I_DPHY_TX_TIME0_REG                0x10
-#define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n)                (((n) & 0xff) << 24)
-#define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n)      (((n) & 0xff) << 16)
-#define SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(n)      ((n) & 0xff)
-
-#define SUN6I_DPHY_TX_TIME1_REG                0x14
-#define SUN6I_DPHY_TX_TIME1_CLK_POST(n)                (((n) & 0xff) << 24)
-#define SUN6I_DPHY_TX_TIME1_CLK_PRE(n)         (((n) & 0xff) << 16)
-#define SUN6I_DPHY_TX_TIME1_CLK_ZERO(n)                (((n) & 0xff) << 8)
-#define SUN6I_DPHY_TX_TIME1_CLK_PREPARE(n)     ((n) & 0xff)
-
-#define SUN6I_DPHY_TX_TIME2_REG                0x18
-#define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n)       ((n) & 0xff)
-
-#define SUN6I_DPHY_TX_TIME3_REG                0x1c
-
-#define SUN6I_DPHY_TX_TIME4_REG                0x20
-#define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n)      (((n) & 0xff) << 8)
-#define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n)      ((n) & 0xff)
-
-#define SUN6I_DPHY_ANA0_REG            0x4c
-#define SUN6I_DPHY_ANA0_REG_PWS                        BIT(31)
-#define SUN6I_DPHY_ANA0_REG_DMPC               BIT(28)
-#define SUN6I_DPHY_ANA0_REG_DMPD(n)            (((n) & 0xf) << 24)
-#define SUN6I_DPHY_ANA0_REG_SLV(n)             (((n) & 7) << 12)
-#define SUN6I_DPHY_ANA0_REG_DEN(n)             (((n) & 0xf) << 8)
-
-#define SUN6I_DPHY_ANA1_REG            0x50
-#define SUN6I_DPHY_ANA1_REG_VTTMODE            BIT(31)
-#define SUN6I_DPHY_ANA1_REG_CSMPS(n)           (((n) & 3) << 28)
-#define SUN6I_DPHY_ANA1_REG_SVTT(n)            (((n) & 0xf) << 24)
-
-#define SUN6I_DPHY_ANA2_REG            0x54
-#define SUN6I_DPHY_ANA2_EN_P2S_CPU(n)          (((n) & 0xf) << 24)
-#define SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK                GENMASK(27, 24)
-#define SUN6I_DPHY_ANA2_EN_CK_CPU              BIT(4)
-#define SUN6I_DPHY_ANA2_REG_ENIB               BIT(1)
-
-#define SUN6I_DPHY_ANA3_REG            0x58
-#define SUN6I_DPHY_ANA3_EN_VTTD(n)             (((n) & 0xf) << 28)
-#define SUN6I_DPHY_ANA3_EN_VTTD_MASK           GENMASK(31, 28)
-#define SUN6I_DPHY_ANA3_EN_VTTC                        BIT(27)
-#define SUN6I_DPHY_ANA3_EN_DIV                 BIT(26)
-#define SUN6I_DPHY_ANA3_EN_LDOC                        BIT(25)
-#define SUN6I_DPHY_ANA3_EN_LDOD                        BIT(24)
-#define SUN6I_DPHY_ANA3_EN_LDOR                        BIT(18)
-
-#define SUN6I_DPHY_ANA4_REG            0x5c
-#define SUN6I_DPHY_ANA4_REG_DMPLVC             BIT(24)
-#define SUN6I_DPHY_ANA4_REG_DMPLVD(n)          (((n) & 0xf) << 20)
-#define SUN6I_DPHY_ANA4_REG_CKDV(n)            (((n) & 0x1f) << 12)
-#define SUN6I_DPHY_ANA4_REG_TMSC(n)            (((n) & 3) << 10)
-#define SUN6I_DPHY_ANA4_REG_TMSD(n)            (((n) & 3) << 8)
-#define SUN6I_DPHY_ANA4_REG_TXDNSC(n)          (((n) & 3) << 6)
-#define SUN6I_DPHY_ANA4_REG_TXDNSD(n)          (((n) & 3) << 4)
-#define SUN6I_DPHY_ANA4_REG_TXPUSC(n)          (((n) & 3) << 2)
-#define SUN6I_DPHY_ANA4_REG_TXPUSD(n)          ((n) & 3)
-
-#define SUN6I_DPHY_DBG5_REG            0xf4
-
-struct sun6i_dphy {
-       struct clk                              *bus_clk;
-       struct clk                              *mod_clk;
-       struct regmap                           *regs;
-       struct reset_control                    *reset;
-
-       struct phy                              *phy;
-       struct phy_configure_opts_mipi_dphy     config;
-};
-
-static int sun6i_dphy_init(struct phy *phy)
-{
-       struct sun6i_dphy *dphy = phy_get_drvdata(phy);
-
-       reset_control_deassert(dphy->reset);
-       clk_prepare_enable(dphy->mod_clk);
-       clk_set_rate_exclusive(dphy->mod_clk, 150000000);
-
-       return 0;
-}
-
-static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
-{
-       struct sun6i_dphy *dphy = phy_get_drvdata(phy);
-       int ret;
-
-       ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
-       if (ret)
-               return ret;
-
-       memcpy(&dphy->config, opts, sizeof(dphy->config));
-
-       return 0;
-}
-
-static int sun6i_dphy_power_on(struct phy *phy)
-{
-       struct sun6i_dphy *dphy = phy_get_drvdata(phy);
-       u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
-
-       regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
-                    SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);
-
-       regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME0_REG,
-                    SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) |
-                    SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) |
-                    SUN6I_DPHY_TX_TIME0_HS_TRAIL(10));
-
-       regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME1_REG,
-                    SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) |
-                    SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) |
-                    SUN6I_DPHY_TX_TIME1_CLK_PRE(3) |
-                    SUN6I_DPHY_TX_TIME1_CLK_POST(10));
-
-       regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME2_REG,
-                    SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30));
-
-       regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME3_REG, 0);
-
-       regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME4_REG,
-                    SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) |
-                    SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
-
-       regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
-                    SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
-                    SUN6I_DPHY_GCTL_EN);
-
-       regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
-                    SUN6I_DPHY_ANA0_REG_PWS |
-                    SUN6I_DPHY_ANA0_REG_DMPC |
-                    SUN6I_DPHY_ANA0_REG_SLV(7) |
-                    SUN6I_DPHY_ANA0_REG_DMPD(lanes_mask) |
-                    SUN6I_DPHY_ANA0_REG_DEN(lanes_mask));
-
-       regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
-                    SUN6I_DPHY_ANA1_REG_CSMPS(1) |
-                    SUN6I_DPHY_ANA1_REG_SVTT(7));
-
-       regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
-                    SUN6I_DPHY_ANA4_REG_CKDV(1) |
-                    SUN6I_DPHY_ANA4_REG_TMSC(1) |
-                    SUN6I_DPHY_ANA4_REG_TMSD(1) |
-                    SUN6I_DPHY_ANA4_REG_TXDNSC(1) |
-                    SUN6I_DPHY_ANA4_REG_TXDNSD(1) |
-                    SUN6I_DPHY_ANA4_REG_TXPUSC(1) |
-                    SUN6I_DPHY_ANA4_REG_TXPUSD(1) |
-                    SUN6I_DPHY_ANA4_REG_DMPLVC |
-                    SUN6I_DPHY_ANA4_REG_DMPLVD(lanes_mask));
-
-       regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
-                    SUN6I_DPHY_ANA2_REG_ENIB);
-       udelay(5);
-
-       regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
-                    SUN6I_DPHY_ANA3_EN_LDOR |
-                    SUN6I_DPHY_ANA3_EN_LDOC |
-                    SUN6I_DPHY_ANA3_EN_LDOD);
-       udelay(1);
-
-       regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
-                          SUN6I_DPHY_ANA3_EN_VTTC |
-                          SUN6I_DPHY_ANA3_EN_VTTD_MASK,
-                          SUN6I_DPHY_ANA3_EN_VTTC |
-                          SUN6I_DPHY_ANA3_EN_VTTD(lanes_mask));
-       udelay(1);
-
-       regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
-                          SUN6I_DPHY_ANA3_EN_DIV,
-                          SUN6I_DPHY_ANA3_EN_DIV);
-       udelay(1);
-
-       regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
-                          SUN6I_DPHY_ANA2_EN_CK_CPU,
-                          SUN6I_DPHY_ANA2_EN_CK_CPU);
-       udelay(1);
-
-       regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
-                          SUN6I_DPHY_ANA1_REG_VTTMODE,
-                          SUN6I_DPHY_ANA1_REG_VTTMODE);
-
-       regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
-                          SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK,
-                          SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask));
-
-       return 0;
-}
-
-static int sun6i_dphy_power_off(struct phy *phy)
-{
-       struct sun6i_dphy *dphy = phy_get_drvdata(phy);
-
-       regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
-                          SUN6I_DPHY_ANA1_REG_VTTMODE, 0);
-
-       return 0;
-}
-
-static int sun6i_dphy_exit(struct phy *phy)
-{
-       struct sun6i_dphy *dphy = phy_get_drvdata(phy);
-
-       clk_rate_exclusive_put(dphy->mod_clk);
-       clk_disable_unprepare(dphy->mod_clk);
-       reset_control_assert(dphy->reset);
-
-       return 0;
-}
-
-
-static struct phy_ops sun6i_dphy_ops = {
-       .configure      = sun6i_dphy_configure,
-       .power_on       = sun6i_dphy_power_on,
-       .power_off      = sun6i_dphy_power_off,
-       .init           = sun6i_dphy_init,
-       .exit           = sun6i_dphy_exit,
-};
-
-static struct regmap_config sun6i_dphy_regmap_config = {
-       .reg_bits       = 32,
-       .val_bits       = 32,
-       .reg_stride     = 4,
-       .max_register   = SUN6I_DPHY_DBG5_REG,
-       .name           = "mipi-dphy",
-};
-
-static int sun6i_dphy_probe(struct platform_device *pdev)
-{
-       struct phy_provider *phy_provider;
-       struct sun6i_dphy *dphy;
-       struct resource *res;
-       void __iomem *regs;
-
-       dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
-       if (!dphy)
-               return -ENOMEM;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       regs = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(regs)) {
-               dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n");
-               return PTR_ERR(regs);
-       }
-
-       dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus",
-                                              regs, &sun6i_dphy_regmap_config);
-       if (IS_ERR(dphy->regs)) {
-               dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n");
-               return PTR_ERR(dphy->regs);
-       }
-
-       dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
-       if (IS_ERR(dphy->reset)) {
-               dev_err(&pdev->dev, "Couldn't get our reset line\n");
-               return PTR_ERR(dphy->reset);
-       }
-
-       dphy->mod_clk = devm_clk_get(&pdev->dev, "mod");
-       if (IS_ERR(dphy->mod_clk)) {
-               dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n");
-               return PTR_ERR(dphy->mod_clk);
-       }
-
-       dphy->phy = devm_phy_create(&pdev->dev, NULL, &sun6i_dphy_ops);
-       if (IS_ERR(dphy->phy)) {
-               dev_err(&pdev->dev, "failed to create PHY\n");
-               return PTR_ERR(dphy->phy);
-       }
-
-       phy_set_drvdata(dphy->phy, dphy);
-       phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
-
-       return PTR_ERR_OR_ZERO(phy_provider);
-}
-
-static const struct of_device_id sun6i_dphy_of_table[] = {
-       { .compatible = "allwinner,sun6i-a31-mipi-dphy" },
-       { }
-};
-MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table);
-
-static struct platform_driver sun6i_dphy_platform_driver = {
-       .probe          = sun6i_dphy_probe,
-       .driver         = {
-               .name           = "sun6i-mipi-dphy",
-               .of_match_table = sun6i_dphy_of_table,
-       },
-};
-module_platform_driver(sun6i_dphy_platform_driver);
-
-MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>");
-MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver");
-MODULE_LICENSE("GPL");
index cdc1e745ba47f052d7a9e43880de7b6e741e2be7..fb1204bcc4548a23d2864b011cf8eefb9eb84e5f 100644 (file)
@@ -17,6 +17,18 @@ config PHY_SUN4I_USB
          This driver controls the entire USB PHY block, both the USB OTG
          parts, as well as the 2 regular USB 2 host PHYs.
 
+config PHY_SUN6I_MIPI_DPHY
+       tristate "Allwinner A31 MIPI D-PHY Support"
+       depends on ARCH_SUNXI && HAS_IOMEM && OF
+       depends on RESET_CONTROLLER
+       select GENERIC_PHY
+       select GENERIC_PHY_MIPI_DPHY
+       select REGMAP_MMIO
+       help
+         Choose this option if you have an Allwinner SoC with
+         MIPI-DSI support. If M is selected, the module will be
+         called sun6i_mipi_dphy.
+
 config PHY_SUN9I_USB
        tristate "Allwinner sun9i SoC USB PHY driver"
        depends on ARCH_SUNXI && HAS_IOMEM && OF
index 8605529c01a11bb7e41ef349e7f5f38fb84aef9b..7d0053efbfaa640af8a5b9b02704f7548d7f38a6 100644 (file)
@@ -1,2 +1,3 @@
 obj-$(CONFIG_PHY_SUN4I_USB)            += phy-sun4i-usb.o
+obj-$(CONFIG_PHY_SUN6I_MIPI_DPHY)      += phy-sun6i-mipi-dphy.o
 obj-$(CONFIG_PHY_SUN9I_USB)            += phy-sun9i-usb.o
diff --git a/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
new file mode 100644 (file)
index 0000000..79c8af5
--- /dev/null
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2016 Allwinnertech Co., Ltd.
+ * Copyright (C) 2017-2018 Bootlin
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+
+#define SUN6I_DPHY_GCTL_REG            0x00
+#define SUN6I_DPHY_GCTL_LANE_NUM(n)            ((((n) - 1) & 3) << 4)
+#define SUN6I_DPHY_GCTL_EN                     BIT(0)
+
+#define SUN6I_DPHY_TX_CTL_REG          0x04
+#define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT       BIT(28)
+
+#define SUN6I_DPHY_TX_TIME0_REG                0x10
+#define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n)                (((n) & 0xff) << 24)
+#define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n)      (((n) & 0xff) << 16)
+#define SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(n)      ((n) & 0xff)
+
+#define SUN6I_DPHY_TX_TIME1_REG                0x14
+#define SUN6I_DPHY_TX_TIME1_CLK_POST(n)                (((n) & 0xff) << 24)
+#define SUN6I_DPHY_TX_TIME1_CLK_PRE(n)         (((n) & 0xff) << 16)
+#define SUN6I_DPHY_TX_TIME1_CLK_ZERO(n)                (((n) & 0xff) << 8)
+#define SUN6I_DPHY_TX_TIME1_CLK_PREPARE(n)     ((n) & 0xff)
+
+#define SUN6I_DPHY_TX_TIME2_REG                0x18
+#define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n)       ((n) & 0xff)
+
+#define SUN6I_DPHY_TX_TIME3_REG                0x1c
+
+#define SUN6I_DPHY_TX_TIME4_REG                0x20
+#define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n)      (((n) & 0xff) << 8)
+#define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n)      ((n) & 0xff)
+
+#define SUN6I_DPHY_ANA0_REG            0x4c
+#define SUN6I_DPHY_ANA0_REG_PWS                        BIT(31)
+#define SUN6I_DPHY_ANA0_REG_DMPC               BIT(28)
+#define SUN6I_DPHY_ANA0_REG_DMPD(n)            (((n) & 0xf) << 24)
+#define SUN6I_DPHY_ANA0_REG_SLV(n)             (((n) & 7) << 12)
+#define SUN6I_DPHY_ANA0_REG_DEN(n)             (((n) & 0xf) << 8)
+
+#define SUN6I_DPHY_ANA1_REG            0x50
+#define SUN6I_DPHY_ANA1_REG_VTTMODE            BIT(31)
+#define SUN6I_DPHY_ANA1_REG_CSMPS(n)           (((n) & 3) << 28)
+#define SUN6I_DPHY_ANA1_REG_SVTT(n)            (((n) & 0xf) << 24)
+
+#define SUN6I_DPHY_ANA2_REG            0x54
+#define SUN6I_DPHY_ANA2_EN_P2S_CPU(n)          (((n) & 0xf) << 24)
+#define SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK                GENMASK(27, 24)
+#define SUN6I_DPHY_ANA2_EN_CK_CPU              BIT(4)
+#define SUN6I_DPHY_ANA2_REG_ENIB               BIT(1)
+
+#define SUN6I_DPHY_ANA3_REG            0x58
+#define SUN6I_DPHY_ANA3_EN_VTTD(n)             (((n) & 0xf) << 28)
+#define SUN6I_DPHY_ANA3_EN_VTTD_MASK           GENMASK(31, 28)
+#define SUN6I_DPHY_ANA3_EN_VTTC                        BIT(27)
+#define SUN6I_DPHY_ANA3_EN_DIV                 BIT(26)
+#define SUN6I_DPHY_ANA3_EN_LDOC                        BIT(25)
+#define SUN6I_DPHY_ANA3_EN_LDOD                        BIT(24)
+#define SUN6I_DPHY_ANA3_EN_LDOR                        BIT(18)
+
+#define SUN6I_DPHY_ANA4_REG            0x5c
+#define SUN6I_DPHY_ANA4_REG_DMPLVC             BIT(24)
+#define SUN6I_DPHY_ANA4_REG_DMPLVD(n)          (((n) & 0xf) << 20)
+#define SUN6I_DPHY_ANA4_REG_CKDV(n)            (((n) & 0x1f) << 12)
+#define SUN6I_DPHY_ANA4_REG_TMSC(n)            (((n) & 3) << 10)
+#define SUN6I_DPHY_ANA4_REG_TMSD(n)            (((n) & 3) << 8)
+#define SUN6I_DPHY_ANA4_REG_TXDNSC(n)          (((n) & 3) << 6)
+#define SUN6I_DPHY_ANA4_REG_TXDNSD(n)          (((n) & 3) << 4)
+#define SUN6I_DPHY_ANA4_REG_TXPUSC(n)          (((n) & 3) << 2)
+#define SUN6I_DPHY_ANA4_REG_TXPUSD(n)          ((n) & 3)
+
+#define SUN6I_DPHY_DBG5_REG            0xf4
+
+struct sun6i_dphy {
+       struct clk                              *bus_clk;
+       struct clk                              *mod_clk;
+       struct regmap                           *regs;
+       struct reset_control                    *reset;
+
+       struct phy                              *phy;
+       struct phy_configure_opts_mipi_dphy     config;
+};
+
+static int sun6i_dphy_init(struct phy *phy)
+{
+       struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+
+       reset_control_deassert(dphy->reset);
+       clk_prepare_enable(dphy->mod_clk);
+       clk_set_rate_exclusive(dphy->mod_clk, 150000000);
+
+       return 0;
+}
+
+static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+       struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+       int ret;
+
+       ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
+       if (ret)
+               return ret;
+
+       memcpy(&dphy->config, opts, sizeof(dphy->config));
+
+       return 0;
+}
+
+static int sun6i_dphy_power_on(struct phy *phy)
+{
+       struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+       u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
+
+       regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
+                    SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);
+
+       regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME0_REG,
+                    SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) |
+                    SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) |
+                    SUN6I_DPHY_TX_TIME0_HS_TRAIL(10));
+
+       regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME1_REG,
+                    SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) |
+                    SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) |
+                    SUN6I_DPHY_TX_TIME1_CLK_PRE(3) |
+                    SUN6I_DPHY_TX_TIME1_CLK_POST(10));
+
+       regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME2_REG,
+                    SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30));
+
+       regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME3_REG, 0);
+
+       regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME4_REG,
+                    SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) |
+                    SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
+
+       regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
+                    SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
+                    SUN6I_DPHY_GCTL_EN);
+
+       regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
+                    SUN6I_DPHY_ANA0_REG_PWS |
+                    SUN6I_DPHY_ANA0_REG_DMPC |
+                    SUN6I_DPHY_ANA0_REG_SLV(7) |
+                    SUN6I_DPHY_ANA0_REG_DMPD(lanes_mask) |
+                    SUN6I_DPHY_ANA0_REG_DEN(lanes_mask));
+
+       regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
+                    SUN6I_DPHY_ANA1_REG_CSMPS(1) |
+                    SUN6I_DPHY_ANA1_REG_SVTT(7));
+
+       regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
+                    SUN6I_DPHY_ANA4_REG_CKDV(1) |
+                    SUN6I_DPHY_ANA4_REG_TMSC(1) |
+                    SUN6I_DPHY_ANA4_REG_TMSD(1) |
+                    SUN6I_DPHY_ANA4_REG_TXDNSC(1) |
+                    SUN6I_DPHY_ANA4_REG_TXDNSD(1) |
+                    SUN6I_DPHY_ANA4_REG_TXPUSC(1) |
+                    SUN6I_DPHY_ANA4_REG_TXPUSD(1) |
+                    SUN6I_DPHY_ANA4_REG_DMPLVC |
+                    SUN6I_DPHY_ANA4_REG_DMPLVD(lanes_mask));
+
+       regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
+                    SUN6I_DPHY_ANA2_REG_ENIB);
+       udelay(5);
+
+       regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
+                    SUN6I_DPHY_ANA3_EN_LDOR |
+                    SUN6I_DPHY_ANA3_EN_LDOC |
+                    SUN6I_DPHY_ANA3_EN_LDOD);
+       udelay(1);
+
+       regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
+                          SUN6I_DPHY_ANA3_EN_VTTC |
+                          SUN6I_DPHY_ANA3_EN_VTTD_MASK,
+                          SUN6I_DPHY_ANA3_EN_VTTC |
+                          SUN6I_DPHY_ANA3_EN_VTTD(lanes_mask));
+       udelay(1);
+
+       regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
+                          SUN6I_DPHY_ANA3_EN_DIV,
+                          SUN6I_DPHY_ANA3_EN_DIV);
+       udelay(1);
+
+       regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
+                          SUN6I_DPHY_ANA2_EN_CK_CPU,
+                          SUN6I_DPHY_ANA2_EN_CK_CPU);
+       udelay(1);
+
+       regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
+                          SUN6I_DPHY_ANA1_REG_VTTMODE,
+                          SUN6I_DPHY_ANA1_REG_VTTMODE);
+
+       regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
+                          SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK,
+                          SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask));
+
+       return 0;
+}
+
+static int sun6i_dphy_power_off(struct phy *phy)
+{
+       struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+
+       regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
+                          SUN6I_DPHY_ANA1_REG_VTTMODE, 0);
+
+       return 0;
+}
+
+static int sun6i_dphy_exit(struct phy *phy)
+{
+       struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+
+       clk_rate_exclusive_put(dphy->mod_clk);
+       clk_disable_unprepare(dphy->mod_clk);
+       reset_control_assert(dphy->reset);
+
+       return 0;
+}
+
+
+static struct phy_ops sun6i_dphy_ops = {
+       .configure      = sun6i_dphy_configure,
+       .power_on       = sun6i_dphy_power_on,
+       .power_off      = sun6i_dphy_power_off,
+       .init           = sun6i_dphy_init,
+       .exit           = sun6i_dphy_exit,
+};
+
+static struct regmap_config sun6i_dphy_regmap_config = {
+       .reg_bits       = 32,
+       .val_bits       = 32,
+       .reg_stride     = 4,
+       .max_register   = SUN6I_DPHY_DBG5_REG,
+       .name           = "mipi-dphy",
+};
+
+static int sun6i_dphy_probe(struct platform_device *pdev)
+{
+       struct phy_provider *phy_provider;
+       struct sun6i_dphy *dphy;
+       struct resource *res;
+       void __iomem *regs;
+
+       dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
+       if (!dphy)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(regs)) {
+               dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n");
+               return PTR_ERR(regs);
+       }
+
+       dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus",
+                                              regs, &sun6i_dphy_regmap_config);
+       if (IS_ERR(dphy->regs)) {
+               dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n");
+               return PTR_ERR(dphy->regs);
+       }
+
+       dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
+       if (IS_ERR(dphy->reset)) {
+               dev_err(&pdev->dev, "Couldn't get our reset line\n");
+               return PTR_ERR(dphy->reset);
+       }
+
+       dphy->mod_clk = devm_clk_get(&pdev->dev, "mod");
+       if (IS_ERR(dphy->mod_clk)) {
+               dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n");
+               return PTR_ERR(dphy->mod_clk);
+       }
+
+       dphy->phy = devm_phy_create(&pdev->dev, NULL, &sun6i_dphy_ops);
+       if (IS_ERR(dphy->phy)) {
+               dev_err(&pdev->dev, "failed to create PHY\n");
+               return PTR_ERR(dphy->phy);
+       }
+
+       phy_set_drvdata(dphy->phy, dphy);
+       phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id sun6i_dphy_of_table[] = {
+       { .compatible = "allwinner,sun6i-a31-mipi-dphy" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table);
+
+static struct platform_driver sun6i_dphy_platform_driver = {
+       .probe          = sun6i_dphy_probe,
+       .driver         = {
+               .name           = "sun6i-mipi-dphy",
+               .of_match_table = sun6i_dphy_of_table,
+       },
+};
+module_platform_driver(sun6i_dphy_platform_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>");
+MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver");
+MODULE_LICENSE("GPL");