diff mbox series

[v5,5/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/

Message ID 2447609da5b80f148c79b2b2a263a0e779f3e82f.1548085432.git-series.maxime.ripard@bootlin.com (mailing list archive)
State New, archived
Headers show
Series phy: Add configuration interface for MIPI D-PHY devices | expand

Commit Message

Maxime Ripard Jan. 21, 2019, 3:45 p.m. UTC
Now that our MIPI D-PHY driver has been converted to the phy framework,
let's move it into the drivers/phy directory.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/gpu/drm/sun4i/Kconfig               |  10 +-
 drivers/gpu/drm/sun4i/Makefile              |   1 +-
 drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c     | 318 +---------------------
 drivers/phy/allwinner/Kconfig               |  12 +-
 drivers/phy/allwinner/Makefile              |   1 +-
 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 318 +++++++++++++++++++++-
 6 files changed, 332 insertions(+), 328 deletions(-)
 delete mode 100644 drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
 create mode 100644 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c

Comments

Paul Kocialkowski Feb. 7, 2019, 8:19 a.m. UTC | #1
Hi,

On Mon, 2019-01-21 at 16:45 +0100, Maxime Ripard wrote:
> Now that our MIPI D-PHY driver has been converted to the phy framework,
> let's move it into the drivers/phy directory.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>

Reviewed-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>

Cheers,

Paul

> ---
>  drivers/gpu/drm/sun4i/Kconfig               |  10 +-
>  drivers/gpu/drm/sun4i/Makefile              |   1 +-
>  drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c     | 318 +---------------------
>  drivers/phy/allwinner/Kconfig               |  12 +-
>  drivers/phy/allwinner/Makefile              |   1 +-
>  drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 318 +++++++++++++++++++++-
>  6 files changed, 332 insertions(+), 328 deletions(-)
>  delete mode 100644 drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
>  create mode 100644 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
> 
> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
> index 2b8db82c4bab..1dbbc3a1b763 100644
> --- a/drivers/gpu/drm/sun4i/Kconfig
> +++ b/drivers/gpu/drm/sun4i/Kconfig
> @@ -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
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index 1e2320d824b5..0d04f2447b01 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -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
> index 79c8af5c7c1d..000000000000
> --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
> +++ /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");
> diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig
> index cdc1e745ba47..fb1204bcc454 100644
> --- a/drivers/phy/allwinner/Kconfig
> +++ b/drivers/phy/allwinner/Kconfig
> @@ -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
> diff --git a/drivers/phy/allwinner/Makefile b/drivers/phy/allwinner/Makefile
> index 8605529c01a1..7d0053efbfaa 100644
> --- a/drivers/phy/allwinner/Makefile
> +++ b/drivers/phy/allwinner/Makefile
> @@ -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
> index 000000000000..79c8af5c7c1d
> --- /dev/null
> +++ b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
> @@ -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");
diff mbox series

Patch

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index 2b8db82c4bab..1dbbc3a1b763 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -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
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index 1e2320d824b5..0d04f2447b01 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -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
index 79c8af5c7c1d..000000000000
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
+++ /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");
diff --git a/drivers/phy/allwinner/Kconfig b/drivers/phy/allwinner/Kconfig
index cdc1e745ba47..fb1204bcc454 100644
--- a/drivers/phy/allwinner/Kconfig
+++ b/drivers/phy/allwinner/Kconfig
@@ -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
diff --git a/drivers/phy/allwinner/Makefile b/drivers/phy/allwinner/Makefile
index 8605529c01a1..7d0053efbfaa 100644
--- a/drivers/phy/allwinner/Makefile
+++ b/drivers/phy/allwinner/Makefile
@@ -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
index 000000000000..79c8af5c7c1d
--- /dev/null
+++ b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
@@ -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");