diff mbox series

[v4,4/9] sun6i: dsi: Convert to generic phy handling

Message ID 0c3493b4e9eaf7b4fbe7960d1239a71fc3563eee.1547026369.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. 9, 2019, 9:33 a.m. UTC
Now that we have everything in place in the PHY framework to deal in a
generic way with MIPI D-PHY phys, let's convert our PHY driver and its
associated DSI driver to that new API.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/gpu/drm/sun4i/Kconfig           |  11 +-
 drivers/gpu/drm/sun4i/Makefile          |   6 +-
 drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c | 164 ++++++++++++++-----------
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c  |  31 ++---
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h  |  17 +---
 5 files changed, 126 insertions(+), 103 deletions(-)

Comments

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

On Wed, 2019-01-09 at 10:33 +0100, Maxime Ripard wrote:
> Now that we have everything in place in the PHY framework to deal in a
> generic way with MIPI D-PHY phys, let's convert our PHY driver and its
> associated DSI driver to that new API.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>

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

Cheers,

Paul

> ---
>  drivers/gpu/drm/sun4i/Kconfig           |  11 +-
>  drivers/gpu/drm/sun4i/Makefile          |   6 +-
>  drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c | 164 ++++++++++++++-----------
>  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c  |  31 ++---
>  drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h  |  17 +---
>  5 files changed, 126 insertions(+), 103 deletions(-)
> 
> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
> index c2c042287c19..2b8db82c4bab 100644
> --- a/drivers/gpu/drm/sun4i/Kconfig
> +++ b/drivers/gpu/drm/sun4i/Kconfig
> @@ -45,10 +45,19 @@ config DRM_SUN6I_DSI
>  	default MACH_SUN8I
>  	select CRC_CCITT
>  	select DRM_MIPI_DSI
> +	select DRM_SUN6I_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-dsi
> +	  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"
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index 0eb38ac8e86e..1e2320d824b5 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -24,9 +24,6 @@ sun4i-tcon-y			+= sun4i_lvds.o
>  sun4i-tcon-y			+= sun4i_tcon.o
>  sun4i-tcon-y			+= sun4i_rgb.o
>  
> -sun6i-dsi-y			+= sun6i_mipi_dphy.o
> -sun6i-dsi-y			+= sun6i_mipi_dsi.o
> -
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i-tcon.o
>  obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
> @@ -37,7 +34,8 @@ 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_DSI)	+= sun6i-dsi.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
>  obj-$(CONFIG_DRM_SUN8I_TCON_TOP) += sun8i_tcon_top.o
> diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
> index e4d19431fa0e..79c8af5c7c1d 100644
> --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
> +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
> @@ -8,11 +8,14 @@
>  
>  #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 "sun6i_mipi_dsi.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)
> @@ -81,12 +84,46 @@
>  
>  #define SUN6I_DPHY_DBG5_REG		0xf4
>  
> -int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes)
> +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);
>  
> @@ -111,16 +148,9 @@ int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes)
>  		     SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
>  
>  	regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
> -		     SUN6I_DPHY_GCTL_LANE_NUM(lanes) |
> +		     SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
>  		     SUN6I_DPHY_GCTL_EN);
>  
> -	return 0;
> -}
> -
> -int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes)
> -{
> -	u8 lanes_mask = GENMASK(lanes - 1, 0);
> -
>  	regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
>  		     SUN6I_DPHY_ANA0_REG_PWS |
>  		     SUN6I_DPHY_ANA0_REG_DMPC |
> @@ -181,16 +211,20 @@ int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes)
>  	return 0;
>  }
>  
> -int sun6i_dphy_power_off(struct sun6i_dphy *dphy)
> +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;
>  }
>  
> -int sun6i_dphy_exit(struct sun6i_dphy *dphy)
> +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);
> @@ -198,6 +232,15 @@ int sun6i_dphy_exit(struct sun6i_dphy *dphy)
>  	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,
> @@ -206,87 +249,70 @@ static struct regmap_config sun6i_dphy_regmap_config = {
>  	.name		= "mipi-dphy",
>  };
>  
> -static const struct of_device_id sun6i_dphy_of_table[] = {
> -	{ .compatible = "allwinner,sun6i-a31-mipi-dphy" },
> -	{ }
> -};
> -
> -int sun6i_dphy_probe(struct sun6i_dsi *dsi, struct device_node *node)
> +static int sun6i_dphy_probe(struct platform_device *pdev)
>  {
> +	struct phy_provider *phy_provider;
>  	struct sun6i_dphy *dphy;
> -	struct resource res;
> +	struct resource *res;
>  	void __iomem *regs;
> -	int ret;
> -
> -	if (!of_match_node(sun6i_dphy_of_table, node)) {
> -		dev_err(dsi->dev, "Incompatible D-PHY\n");
> -		return -EINVAL;
> -	}
>  
> -	dphy = devm_kzalloc(dsi->dev, sizeof(*dphy), GFP_KERNEL);
> +	dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
>  	if (!dphy)
>  		return -ENOMEM;
>  
> -	ret = of_address_to_resource(node, 0, &res);
> -	if (ret) {
> -		dev_err(dsi->dev, "phy: Couldn't get our resources\n");
> -		return ret;
> -	}
> -
> -	regs = devm_ioremap_resource(dsi->dev, &res);
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	regs = devm_ioremap_resource(&pdev->dev, res);
>  	if (IS_ERR(regs)) {
> -		dev_err(dsi->dev, "Couldn't map the DPHY encoder registers\n");
> +		dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n");
>  		return PTR_ERR(regs);
>  	}
>  
> -	dphy->regs = devm_regmap_init_mmio(dsi->dev, regs,
> -					   &sun6i_dphy_regmap_config);
> +	dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus",
> +					       regs, &sun6i_dphy_regmap_config);
>  	if (IS_ERR(dphy->regs)) {
> -		dev_err(dsi->dev, "Couldn't create the DPHY encoder regmap\n");
> +		dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n");
>  		return PTR_ERR(dphy->regs);
>  	}
>  
> -	dphy->reset = of_reset_control_get_shared(node, NULL);
> +	dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
>  	if (IS_ERR(dphy->reset)) {
> -		dev_err(dsi->dev, "Couldn't get our reset line\n");
> +		dev_err(&pdev->dev, "Couldn't get our reset line\n");
>  		return PTR_ERR(dphy->reset);
>  	}
>  
> -	dphy->bus_clk = of_clk_get_by_name(node, "bus");
> -	if (IS_ERR(dphy->bus_clk)) {
> -		dev_err(dsi->dev, "Couldn't get the DPHY bus clock\n");
> -		ret = PTR_ERR(dphy->bus_clk);
> -		goto err_free_reset;
> -	}
> -	regmap_mmio_attach_clk(dphy->regs, dphy->bus_clk);
> -
> -	dphy->mod_clk = of_clk_get_by_name(node, "mod");
> +	dphy->mod_clk = devm_clk_get(&pdev->dev, "mod");
>  	if (IS_ERR(dphy->mod_clk)) {
> -		dev_err(dsi->dev, "Couldn't get the DPHY mod clock\n");
> -		ret = PTR_ERR(dphy->mod_clk);
> -		goto err_free_bus;
> +		dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n");
> +		return PTR_ERR(dphy->mod_clk);
>  	}
>  
> -	dsi->dphy = dphy;
> +	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);
> +	}
>  
> -	return 0;
> +	phy_set_drvdata(dphy->phy, dphy);
> +	phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
>  
> -err_free_bus:
> -	regmap_mmio_detach_clk(dphy->regs);
> -	clk_put(dphy->bus_clk);
> -err_free_reset:
> -	reset_control_put(dphy->reset);
> -	return ret;
> +	return PTR_ERR_OR_ZERO(phy_provider);
>  }
>  
> -int sun6i_dphy_remove(struct sun6i_dsi *dsi)
> -{
> -	struct sun6i_dphy *dphy = dsi->dphy;
> -
> -	regmap_mmio_detach_clk(dphy->regs);
> -	clk_put(dphy->mod_clk);
> -	clk_put(dphy->bus_clk);
> -	reset_control_put(dphy->reset);
> +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);
>  
> -	return 0;
> -}
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>");
> +MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> index e3b34a345546..7bbce7708265 100644
> --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> @@ -16,6 +16,7 @@
>  #include <linux/slab.h>
>  
>  #include <linux/phy/phy.h>
> +#include <linux/phy/phy-mipi-dphy.h>
>  
>  #include <drm/drmP.h>
>  #include <drm/drm_atomic_helper.h>
> @@ -616,6 +617,8 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
>  	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
>  	struct sun6i_dsi *dsi = encoder_to_sun6i_dsi(encoder);
>  	struct mipi_dsi_device *device = dsi->device;
> +	union phy_configure_opts opts = { 0 };
> +	struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
>  	u16 delay;
>  
>  	DRM_DEBUG_DRIVER("Enabling DSI output\n");
> @@ -634,8 +637,15 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
>  	sun6i_dsi_setup_format(dsi, mode);
>  	sun6i_dsi_setup_timings(dsi, mode);
>  
> -	sun6i_dphy_init(dsi->dphy, device->lanes);
> -	sun6i_dphy_power_on(dsi->dphy, device->lanes);
> +	phy_init(dsi->dphy);
> +
> +	phy_mipi_dphy_get_default_config(mode->clock * 1000,
> +					 mipi_dsi_pixel_format_to_bpp(device->format),
> +					 device->lanes, cfg);
> +
> +	phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY);
> +	phy_configure(dsi->dphy, &opts);
> +	phy_power_on(dsi->dphy);
>  
>  	if (!IS_ERR(dsi->panel))
>  		drm_panel_prepare(dsi->panel);
> @@ -673,8 +683,8 @@ static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder)
>  		drm_panel_unprepare(dsi->panel);
>  	}
>  
> -	sun6i_dphy_power_off(dsi->dphy);
> -	sun6i_dphy_exit(dsi->dphy);
> +	phy_power_off(dsi->dphy);
> +	phy_exit(dsi->dphy);
>  
>  	pm_runtime_put(dsi->dev);
>  }
> @@ -967,7 +977,6 @@ static const struct component_ops sun6i_dsi_ops = {
>  static int sun6i_dsi_probe(struct platform_device *pdev)
>  {
>  	struct device *dev = &pdev->dev;
> -	struct device_node *dphy_node;
>  	struct sun6i_dsi *dsi;
>  	struct resource *res;
>  	void __iomem *base;
> @@ -1013,10 +1022,8 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
>  	 */
>  	clk_set_rate_exclusive(dsi->mod_clk, 297000000);
>  
> -	dphy_node = of_parse_phandle(dev->of_node, "phys", 0);
> -	ret = sun6i_dphy_probe(dsi, dphy_node);
> -	of_node_put(dphy_node);
> -	if (ret) {
> +	dsi->dphy = devm_phy_get(dev, "dphy");
> +	if (IS_ERR(dsi->dphy)) {
>  		dev_err(dev, "Couldn't get the MIPI D-PHY\n");
>  		goto err_unprotect_clk;
>  	}
> @@ -1026,7 +1033,7 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
>  	ret = mipi_dsi_host_register(&dsi->host);
>  	if (ret) {
>  		dev_err(dev, "Couldn't register MIPI-DSI host\n");
> -		goto err_remove_phy;
> +		goto err_pm_disable;
>  	}
>  
>  	ret = component_add(&pdev->dev, &sun6i_dsi_ops);
> @@ -1039,9 +1046,8 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
>  
>  err_remove_dsi_host:
>  	mipi_dsi_host_unregister(&dsi->host);
> -err_remove_phy:
> +err_pm_disable:
>  	pm_runtime_disable(dev);
> -	sun6i_dphy_remove(dsi);
>  err_unprotect_clk:
>  	clk_rate_exclusive_put(dsi->mod_clk);
>  	return ret;
> @@ -1055,7 +1061,6 @@ static int sun6i_dsi_remove(struct platform_device *pdev)
>  	component_del(&pdev->dev, &sun6i_dsi_ops);
>  	mipi_dsi_host_unregister(&dsi->host);
>  	pm_runtime_disable(dev);
> -	sun6i_dphy_remove(dsi);
>  	clk_rate_exclusive_put(dsi->mod_clk);
>  
>  	return 0;
> diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
> index dbbc5b3ecbda..a07090579f84 100644
> --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
> +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
> @@ -13,13 +13,6 @@
>  #include <drm/drm_encoder.h>
>  #include <drm/drm_mipi_dsi.h>
>  
> -struct sun6i_dphy {
> -	struct clk		*bus_clk;
> -	struct clk		*mod_clk;
> -	struct regmap		*regs;
> -	struct reset_control	*reset;
> -};
> -
>  struct sun6i_dsi {
>  	struct drm_connector	connector;
>  	struct drm_encoder	encoder;
> @@ -29,7 +22,7 @@ struct sun6i_dsi {
>  	struct clk		*mod_clk;
>  	struct regmap		*regs;
>  	struct reset_control	*reset;
> -	struct sun6i_dphy	*dphy;
> +	struct phy		*dphy;
>  
>  	struct device		*dev;
>  	struct sun4i_drv	*drv;
> @@ -52,12 +45,4 @@ static inline struct sun6i_dsi *encoder_to_sun6i_dsi(const struct drm_encoder *e
>  	return container_of(encoder, struct sun6i_dsi, encoder);
>  };
>  
> -int sun6i_dphy_probe(struct sun6i_dsi *dsi, struct device_node *node);
> -int sun6i_dphy_remove(struct sun6i_dsi *dsi);
> -
> -int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes);
> -int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes);
> -int sun6i_dphy_power_off(struct sun6i_dphy *dphy);
> -int sun6i_dphy_exit(struct sun6i_dphy *dphy);
> -
>  #endif /* _SUN6I_MIPI_DSI_H_ */
diff mbox series

Patch

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index c2c042287c19..2b8db82c4bab 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -45,10 +45,19 @@  config DRM_SUN6I_DSI
 	default MACH_SUN8I
 	select CRC_CCITT
 	select DRM_MIPI_DSI
+	select DRM_SUN6I_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-dsi
+	  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"
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index 0eb38ac8e86e..1e2320d824b5 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -24,9 +24,6 @@  sun4i-tcon-y			+= sun4i_lvds.o
 sun4i-tcon-y			+= sun4i_tcon.o
 sun4i-tcon-y			+= sun4i_rgb.o
 
-sun6i-dsi-y			+= sun6i_mipi_dphy.o
-sun6i-dsi-y			+= sun6i_mipi_dsi.o
-
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i-drm.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i-tcon.o
 obj-$(CONFIG_DRM_SUN4I)		+= sun4i_tv.o
@@ -37,7 +34,8 @@  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_DSI)	+= sun6i-dsi.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
 obj-$(CONFIG_DRM_SUN8I_TCON_TOP) += sun8i_tcon_top.o
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
index e4d19431fa0e..79c8af5c7c1d 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
@@ -8,11 +8,14 @@ 
 
 #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 "sun6i_mipi_dsi.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)
@@ -81,12 +84,46 @@ 
 
 #define SUN6I_DPHY_DBG5_REG		0xf4
 
-int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes)
+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);
 
@@ -111,16 +148,9 @@  int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes)
 		     SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
 
 	regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
-		     SUN6I_DPHY_GCTL_LANE_NUM(lanes) |
+		     SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
 		     SUN6I_DPHY_GCTL_EN);
 
-	return 0;
-}
-
-int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes)
-{
-	u8 lanes_mask = GENMASK(lanes - 1, 0);
-
 	regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
 		     SUN6I_DPHY_ANA0_REG_PWS |
 		     SUN6I_DPHY_ANA0_REG_DMPC |
@@ -181,16 +211,20 @@  int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes)
 	return 0;
 }
 
-int sun6i_dphy_power_off(struct sun6i_dphy *dphy)
+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;
 }
 
-int sun6i_dphy_exit(struct sun6i_dphy *dphy)
+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);
@@ -198,6 +232,15 @@  int sun6i_dphy_exit(struct sun6i_dphy *dphy)
 	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,
@@ -206,87 +249,70 @@  static struct regmap_config sun6i_dphy_regmap_config = {
 	.name		= "mipi-dphy",
 };
 
-static const struct of_device_id sun6i_dphy_of_table[] = {
-	{ .compatible = "allwinner,sun6i-a31-mipi-dphy" },
-	{ }
-};
-
-int sun6i_dphy_probe(struct sun6i_dsi *dsi, struct device_node *node)
+static int sun6i_dphy_probe(struct platform_device *pdev)
 {
+	struct phy_provider *phy_provider;
 	struct sun6i_dphy *dphy;
-	struct resource res;
+	struct resource *res;
 	void __iomem *regs;
-	int ret;
-
-	if (!of_match_node(sun6i_dphy_of_table, node)) {
-		dev_err(dsi->dev, "Incompatible D-PHY\n");
-		return -EINVAL;
-	}
 
-	dphy = devm_kzalloc(dsi->dev, sizeof(*dphy), GFP_KERNEL);
+	dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
 	if (!dphy)
 		return -ENOMEM;
 
-	ret = of_address_to_resource(node, 0, &res);
-	if (ret) {
-		dev_err(dsi->dev, "phy: Couldn't get our resources\n");
-		return ret;
-	}
-
-	regs = devm_ioremap_resource(dsi->dev, &res);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(regs)) {
-		dev_err(dsi->dev, "Couldn't map the DPHY encoder registers\n");
+		dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n");
 		return PTR_ERR(regs);
 	}
 
-	dphy->regs = devm_regmap_init_mmio(dsi->dev, regs,
-					   &sun6i_dphy_regmap_config);
+	dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus",
+					       regs, &sun6i_dphy_regmap_config);
 	if (IS_ERR(dphy->regs)) {
-		dev_err(dsi->dev, "Couldn't create the DPHY encoder regmap\n");
+		dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n");
 		return PTR_ERR(dphy->regs);
 	}
 
-	dphy->reset = of_reset_control_get_shared(node, NULL);
+	dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
 	if (IS_ERR(dphy->reset)) {
-		dev_err(dsi->dev, "Couldn't get our reset line\n");
+		dev_err(&pdev->dev, "Couldn't get our reset line\n");
 		return PTR_ERR(dphy->reset);
 	}
 
-	dphy->bus_clk = of_clk_get_by_name(node, "bus");
-	if (IS_ERR(dphy->bus_clk)) {
-		dev_err(dsi->dev, "Couldn't get the DPHY bus clock\n");
-		ret = PTR_ERR(dphy->bus_clk);
-		goto err_free_reset;
-	}
-	regmap_mmio_attach_clk(dphy->regs, dphy->bus_clk);
-
-	dphy->mod_clk = of_clk_get_by_name(node, "mod");
+	dphy->mod_clk = devm_clk_get(&pdev->dev, "mod");
 	if (IS_ERR(dphy->mod_clk)) {
-		dev_err(dsi->dev, "Couldn't get the DPHY mod clock\n");
-		ret = PTR_ERR(dphy->mod_clk);
-		goto err_free_bus;
+		dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n");
+		return PTR_ERR(dphy->mod_clk);
 	}
 
-	dsi->dphy = dphy;
+	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);
+	}
 
-	return 0;
+	phy_set_drvdata(dphy->phy, dphy);
+	phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
 
-err_free_bus:
-	regmap_mmio_detach_clk(dphy->regs);
-	clk_put(dphy->bus_clk);
-err_free_reset:
-	reset_control_put(dphy->reset);
-	return ret;
+	return PTR_ERR_OR_ZERO(phy_provider);
 }
 
-int sun6i_dphy_remove(struct sun6i_dsi *dsi)
-{
-	struct sun6i_dphy *dphy = dsi->dphy;
-
-	regmap_mmio_detach_clk(dphy->regs);
-	clk_put(dphy->mod_clk);
-	clk_put(dphy->bus_clk);
-	reset_control_put(dphy->reset);
+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);
 
-	return 0;
-}
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>");
+MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index e3b34a345546..7bbce7708265 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -16,6 +16,7 @@ 
 #include <linux/slab.h>
 
 #include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic_helper.h>
@@ -616,6 +617,8 @@  static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
 	struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
 	struct sun6i_dsi *dsi = encoder_to_sun6i_dsi(encoder);
 	struct mipi_dsi_device *device = dsi->device;
+	union phy_configure_opts opts = { 0 };
+	struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
 	u16 delay;
 
 	DRM_DEBUG_DRIVER("Enabling DSI output\n");
@@ -634,8 +637,15 @@  static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
 	sun6i_dsi_setup_format(dsi, mode);
 	sun6i_dsi_setup_timings(dsi, mode);
 
-	sun6i_dphy_init(dsi->dphy, device->lanes);
-	sun6i_dphy_power_on(dsi->dphy, device->lanes);
+	phy_init(dsi->dphy);
+
+	phy_mipi_dphy_get_default_config(mode->clock * 1000,
+					 mipi_dsi_pixel_format_to_bpp(device->format),
+					 device->lanes, cfg);
+
+	phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY);
+	phy_configure(dsi->dphy, &opts);
+	phy_power_on(dsi->dphy);
 
 	if (!IS_ERR(dsi->panel))
 		drm_panel_prepare(dsi->panel);
@@ -673,8 +683,8 @@  static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder)
 		drm_panel_unprepare(dsi->panel);
 	}
 
-	sun6i_dphy_power_off(dsi->dphy);
-	sun6i_dphy_exit(dsi->dphy);
+	phy_power_off(dsi->dphy);
+	phy_exit(dsi->dphy);
 
 	pm_runtime_put(dsi->dev);
 }
@@ -967,7 +977,6 @@  static const struct component_ops sun6i_dsi_ops = {
 static int sun6i_dsi_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-	struct device_node *dphy_node;
 	struct sun6i_dsi *dsi;
 	struct resource *res;
 	void __iomem *base;
@@ -1013,10 +1022,8 @@  static int sun6i_dsi_probe(struct platform_device *pdev)
 	 */
 	clk_set_rate_exclusive(dsi->mod_clk, 297000000);
 
-	dphy_node = of_parse_phandle(dev->of_node, "phys", 0);
-	ret = sun6i_dphy_probe(dsi, dphy_node);
-	of_node_put(dphy_node);
-	if (ret) {
+	dsi->dphy = devm_phy_get(dev, "dphy");
+	if (IS_ERR(dsi->dphy)) {
 		dev_err(dev, "Couldn't get the MIPI D-PHY\n");
 		goto err_unprotect_clk;
 	}
@@ -1026,7 +1033,7 @@  static int sun6i_dsi_probe(struct platform_device *pdev)
 	ret = mipi_dsi_host_register(&dsi->host);
 	if (ret) {
 		dev_err(dev, "Couldn't register MIPI-DSI host\n");
-		goto err_remove_phy;
+		goto err_pm_disable;
 	}
 
 	ret = component_add(&pdev->dev, &sun6i_dsi_ops);
@@ -1039,9 +1046,8 @@  static int sun6i_dsi_probe(struct platform_device *pdev)
 
 err_remove_dsi_host:
 	mipi_dsi_host_unregister(&dsi->host);
-err_remove_phy:
+err_pm_disable:
 	pm_runtime_disable(dev);
-	sun6i_dphy_remove(dsi);
 err_unprotect_clk:
 	clk_rate_exclusive_put(dsi->mod_clk);
 	return ret;
@@ -1055,7 +1061,6 @@  static int sun6i_dsi_remove(struct platform_device *pdev)
 	component_del(&pdev->dev, &sun6i_dsi_ops);
 	mipi_dsi_host_unregister(&dsi->host);
 	pm_runtime_disable(dev);
-	sun6i_dphy_remove(dsi);
 	clk_rate_exclusive_put(dsi->mod_clk);
 
 	return 0;
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
index dbbc5b3ecbda..a07090579f84 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
@@ -13,13 +13,6 @@ 
 #include <drm/drm_encoder.h>
 #include <drm/drm_mipi_dsi.h>
 
-struct sun6i_dphy {
-	struct clk		*bus_clk;
-	struct clk		*mod_clk;
-	struct regmap		*regs;
-	struct reset_control	*reset;
-};
-
 struct sun6i_dsi {
 	struct drm_connector	connector;
 	struct drm_encoder	encoder;
@@ -29,7 +22,7 @@  struct sun6i_dsi {
 	struct clk		*mod_clk;
 	struct regmap		*regs;
 	struct reset_control	*reset;
-	struct sun6i_dphy	*dphy;
+	struct phy		*dphy;
 
 	struct device		*dev;
 	struct sun4i_drv	*drv;
@@ -52,12 +45,4 @@  static inline struct sun6i_dsi *encoder_to_sun6i_dsi(const struct drm_encoder *e
 	return container_of(encoder, struct sun6i_dsi, encoder);
 };
 
-int sun6i_dphy_probe(struct sun6i_dsi *dsi, struct device_node *node);
-int sun6i_dphy_remove(struct sun6i_dsi *dsi);
-
-int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes);
-int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes);
-int sun6i_dphy_power_off(struct sun6i_dphy *dphy);
-int sun6i_dphy_exit(struct sun6i_dphy *dphy);
-
 #endif /* _SUN6I_MIPI_DSI_H_ */