linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices
@ 2019-01-09  9:33 Maxime Ripard
  2019-01-09  9:33 ` [PATCH v4 1/9] phy: dphy: Remove unused header Maxime Ripard
                   ` (9 more replies)
  0 siblings, 10 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-09  9:33 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

Hi,

Here is a set of patches to allow the phy framework consumers to test and
apply runtime configurations.

This is needed to support more phy classes that require tuning based on
parameters depending on the current use case of the device, in addition to
the power state management already provided by the current functions.

A first test bed for that API are the MIPI D-PHY devices. There's a number
of solutions that have been used so far to support these phy, most of the
time being an ad-hoc driver in the consumer.

That approach has a big shortcoming though, which is that this is quite
difficult to deal with consumers integrated with multiple variants of phy,
of multiple consumers integrated with the same phy.

The latter case can be found in the Cadence DSI bridge, and the CSI
transceiver and receivers. All of them are integrated with the same phy, or
can be integrated with different phy, depending on the implementation.

I've looked at all the MIPI DSI drivers I could find, and gathered all the
parameters I could find. The interface should be complete, and most of the
drivers can be converted in the future. The current set converts two of
them: the above mentionned Cadence DSI driver so that the v4l2 drivers can
use them, and the Allwinner MIPI-DSI driver.

Let me know what you think,
Maxime

Changes from v3
  - Rebased on 5.0-rc1
  - Added the fixes suggested by Sakari

Changes from v2:
  - Rebased on next
  - Changed the interface to accomodate for the new submodes
  - Changed the timings units from nanoseconds to picoseconds
  - Added minimum and maximum boundaries to the documentation
  - Moved the clock enabling to phy_power_on in the Cadence DPHY driver
  - Exported the phy_configure and phy_validate symbols
  - Rework the phy pll divider computation in the cadence dphy driver

Changes from v1:
  - Rebased on top of 4.20-rc1
  - Removed the bus mode and timings parameters from the MIPI D-PHY
    parameters, since that shouldn't have any impact on the PHY itself.
  - Reworked the Cadence DSI and D-PHY drivers to take this into account.
  - Remove the mode parameter from phy_configure
  - Added phy_configure and phy_validate stubs
  - Return -EOPNOTSUPP in phy_configure and phy_validate when the operation
    is not implemented

Maxime Ripard (9):
  phy: dphy: Remove unused header
  phy: dphy: Change units of wakeup and init parameters
  phy: dphy: Clarify lanes parameter documentation
  sun6i: dsi: Convert to generic phy handling
  phy: Move Allwinner A31 D-PHY driver to drivers/phy/
  drm/bridge: cdns: Separate DSI and D-PHY configuration
  dt-bindings: phy: Move the Cadence D-PHY bindings
  phy: Add Cadence D-PHY support
  drm/bridge: cdns: Convert to phy framework

 Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt |  21 +-
 Documentation/devicetree/bindings/phy/cdns,dphy.txt           |  20 +-
 drivers/gpu/drm/bridge/Kconfig                                |   1 +-
 drivers/gpu/drm/bridge/cdns-dsi.c                             | 535 +------
 drivers/gpu/drm/sun4i/Kconfig                                 |   3 +-
 drivers/gpu/drm/sun4i/Makefile                                |   5 +-
 drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c                       | 292 +----
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c                        |  31 +-
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h                        |  17 +-
 drivers/phy/allwinner/Kconfig                                 |  12 +-
 drivers/phy/allwinner/Makefile                                |   1 +-
 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c                   | 318 ++++-
 drivers/phy/cadence/Kconfig                                   |  13 +-
 drivers/phy/cadence/Makefile                                  |   1 +-
 drivers/phy/cadence/cdns-dphy.c                               | 389 +++++-
 drivers/phy/phy-core-mipi-dphy.c                              |   8 +-
 include/linux/phy/phy-mipi-dphy.h                             |  13 +-
 17 files changed, 890 insertions(+), 790 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/phy/cdns,dphy.txt
 delete mode 100644 drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
 create mode 100644 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
 create mode 100644 drivers/phy/cadence/cdns-dphy.c

base-commit: bfeffd155283772bbe78c6a05dec7c0128ee500c
-- 
git-series 0.9.1

^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH v4 1/9] phy: dphy: Remove unused header
  2019-01-09  9:33 [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
@ 2019-01-09  9:33 ` Maxime Ripard
  2019-01-09  9:33 ` [PATCH v4 2/9] phy: dphy: Change units of wakeup and init parameters Maxime Ripard
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-09  9:33 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard,
	Sakari Ailus

The videomode.h header inclusion is an artifact from the patches
development, remove it.

Suggested-by: Sakari Ailus <sakari.ailus@iki.fi>
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 include/linux/phy/phy-mipi-dphy.h | 2 --
 1 file changed, 2 deletions(-)

diff --git a/include/linux/phy/phy-mipi-dphy.h b/include/linux/phy/phy-mipi-dphy.h
index c08aacc0ac35..9cf97cd1d303 100644
--- a/include/linux/phy/phy-mipi-dphy.h
+++ b/include/linux/phy/phy-mipi-dphy.h
@@ -6,8 +6,6 @@
 #ifndef __PHY_MIPI_DPHY_H_
 #define __PHY_MIPI_DPHY_H_
 
-#include <video/videomode.h>
-
 /**
  * struct phy_configure_opts_mipi_dphy - MIPI D-PHY configuration set
  *
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 2/9] phy: dphy: Change units of wakeup and init parameters
  2019-01-09  9:33 [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
  2019-01-09  9:33 ` [PATCH v4 1/9] phy: dphy: Remove unused header Maxime Ripard
@ 2019-01-09  9:33 ` Maxime Ripard
  2019-01-09  9:33 ` [PATCH v4 3/9] phy: dphy: Clarify lanes parameter documentation Maxime Ripard
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-09  9:33 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard,
	Sakari Ailus

The Init and wakeup D-PHY parameters are in the micro/milliseconds range,
putting the values real close to the types limits if they were in
picoseconds.

Move them to microseconds which should be better fit.

Suggested-by: Sakari Ailus <sakari.ailus@iki.fi>
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/phy/phy-core-mipi-dphy.c  | 8 ++++----
 include/linux/phy/phy-mipi-dphy.h | 8 ++++----
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c
index 465fa1b91a5f..14e0551cd319 100644
--- a/drivers/phy/phy-core-mipi-dphy.c
+++ b/drivers/phy/phy-core-mipi-dphy.c
@@ -65,12 +65,12 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
 	 */
 	cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui);
 
-	cfg->init = 100000000;
+	cfg->init = 100;
 	cfg->lpx = 60000;
 	cfg->ta_get = 5 * cfg->lpx;
 	cfg->ta_go = 4 * cfg->lpx;
 	cfg->ta_sure = 2 * cfg->lpx;
-	cfg->wakeup = 1000000000;
+	cfg->wakeup = 1000;
 
 	cfg->hs_clk_rate = hs_clk_rate;
 	cfg->lanes = lanes;
@@ -143,7 +143,7 @@ int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
 	if (cfg->hs_trail < max(8 * ui, 60000 + 4 * ui))
 		return -EINVAL;
 
-	if (cfg->init < 100000000)
+	if (cfg->init < 100)
 		return -EINVAL;
 
 	if (cfg->lpx < 50000)
@@ -158,7 +158,7 @@ int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
 	if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > (2 * cfg->lpx))
 		return -EINVAL;
 
-	if (cfg->wakeup < 1000000000)
+	if (cfg->wakeup < 1000)
 		return -EINVAL;
 
 	return 0;
diff --git a/include/linux/phy/phy-mipi-dphy.h b/include/linux/phy/phy-mipi-dphy.h
index 9cf97cd1d303..627d28080d3a 100644
--- a/include/linux/phy/phy-mipi-dphy.h
+++ b/include/linux/phy/phy-mipi-dphy.h
@@ -190,10 +190,10 @@ struct phy_configure_opts_mipi_dphy {
 	/**
 	 * @init:
 	 *
-	 * Time, in picoseconds for the initialization period to
+	 * Time, in microseconds for the initialization period to
 	 * complete.
 	 *
-	 * Minimum value: 100000000 ps
+	 * Minimum value: 100 us
 	 */
 	unsigned int		init;
 
@@ -244,11 +244,11 @@ struct phy_configure_opts_mipi_dphy {
 	/**
 	 * @wakeup:
 	 *
-	 * Time, in picoseconds, that a transmitter drives a Mark-1
+	 * Time, in microseconds, that a transmitter drives a Mark-1
 	 * state prior to a Stop state in order to initiate an exit
 	 * from ULPS.
 	 *
-	 * Minimum value: 1000000000 ps
+	 * Minimum value: 1000 us
 	 */
 	unsigned int		wakeup;
 
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 3/9] phy: dphy: Clarify lanes parameter documentation
  2019-01-09  9:33 [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
  2019-01-09  9:33 ` [PATCH v4 1/9] phy: dphy: Remove unused header Maxime Ripard
  2019-01-09  9:33 ` [PATCH v4 2/9] phy: dphy: Change units of wakeup and init parameters Maxime Ripard
@ 2019-01-09  9:33 ` Maxime Ripard
  2019-01-09  9:33 ` [PATCH v4 4/9] sun6i: dsi: Convert to generic phy handling Maxime Ripard
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-09  9:33 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard,
	Sakari Ailus

The lanes parameter is not solely about the number of lanes, but it also
carries the fact that those are the first lanes in use during the
transmission.

It was implicit so far, so make sure it's explicit now.

Suggested-by: Sakari Ailus <sakari.ailus@iki.fi>
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 include/linux/phy/phy-mipi-dphy.h | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/include/linux/phy/phy-mipi-dphy.h b/include/linux/phy/phy-mipi-dphy.h
index 627d28080d3a..a877ffee845d 100644
--- a/include/linux/phy/phy-mipi-dphy.h
+++ b/include/linux/phy/phy-mipi-dphy.h
@@ -269,7 +269,8 @@ struct phy_configure_opts_mipi_dphy {
 	/**
 	 * @lanes:
 	 *
-	 * Number of active data lanes used for the transmissions.
+	 * Number of active, consecutive, data lanes, starting from
+	 * lane 0, used for the transmissions.
 	 */
 	unsigned char		lanes;
 };
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 4/9] sun6i: dsi: Convert to generic phy handling
  2019-01-09  9:33 [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (2 preceding siblings ...)
  2019-01-09  9:33 ` [PATCH v4 3/9] phy: dphy: Clarify lanes parameter documentation Maxime Ripard
@ 2019-01-09  9:33 ` Maxime Ripard
  2019-02-07  8:16   ` Paul Kocialkowski
  2019-01-09  9:33 ` [PATCH v4 5/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/ Maxime Ripard
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Maxime Ripard @ 2019-01-09  9:33 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

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(-)

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_ */
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 5/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/
  2019-01-09  9:33 [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (3 preceding siblings ...)
  2019-01-09  9:33 ` [PATCH v4 4/9] sun6i: dsi: Convert to generic phy handling Maxime Ripard
@ 2019-01-09  9:33 ` Maxime Ripard
  2019-01-09  9:33 ` [PATCH v4 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-09  9:33 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

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

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");
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration
  2019-01-09  9:33 [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (4 preceding siblings ...)
  2019-01-09  9:33 ` [PATCH v4 5/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/ Maxime Ripard
@ 2019-01-09  9:33 ` Maxime Ripard
  2019-01-16  8:36   ` Kishon Vijay Abraham I
  2019-01-17 13:33   ` Sean Paul
  2019-01-09  9:33 ` [PATCH v4 7/9] dt-bindings: phy: Move the Cadence D-PHY bindings Maxime Ripard
                   ` (3 subsequent siblings)
  9 siblings, 2 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-09  9:33 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

The current configuration of the DSI bridge and its associated D-PHY is
intertwined. In order to ease the future conversion to the phy framework
for the D-PHY part, let's split the configuration in two.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/gpu/drm/bridge/cdns-dsi.c | 96 ++++++++++++++++++++++----------
 1 file changed, 68 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
index ce9496d13986..3ac6dd524b6d 100644
--- a/drivers/gpu/drm/bridge/cdns-dsi.c
+++ b/drivers/gpu/drm/bridge/cdns-dsi.c
@@ -545,6 +545,11 @@ bridge_to_cdns_dsi_input(struct drm_bridge *bridge)
 	return container_of(bridge, struct cdns_dsi_input, bridge);
 }
 
+static unsigned int mode_to_dpi_hfp(const struct drm_display_mode *mode)
+{
+	return mode->crtc_hsync_start - mode->crtc_hdisplay;
+}
+
 static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
 				     struct cdns_dphy_cfg *cfg,
 				     unsigned int dpi_htotal,
@@ -731,14 +736,12 @@ static unsigned int dpi_to_dsi_timing(unsigned int dpi_timing,
 static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
 			     const struct drm_display_mode *mode,
 			     struct cdns_dsi_cfg *dsi_cfg,
-			     struct cdns_dphy_cfg *dphy_cfg,
 			     bool mode_valid_check)
 {
-	unsigned long dsi_htotal = 0, dsi_hss_hsa_hse_hbp = 0;
 	struct cdns_dsi_output *output = &dsi->output;
-	unsigned int dsi_hfp_ext = 0, dpi_hfp, tmp;
+	unsigned int tmp;
 	bool sync_pulse = false;
-	int bpp, nlanes, ret;
+	int bpp, nlanes;
 
 	memset(dsi_cfg, 0, sizeof(*dsi_cfg));
 
@@ -757,8 +760,6 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
 		       mode->crtc_hsync_end : mode->crtc_hsync_start);
 
 	dsi_cfg->hbp = dpi_to_dsi_timing(tmp, bpp, DSI_HBP_FRAME_OVERHEAD);
-	dsi_htotal += dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
-	dsi_hss_hsa_hse_hbp += dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
 
 	if (sync_pulse) {
 		if (mode_valid_check)
@@ -768,49 +769,90 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
 
 		dsi_cfg->hsa = dpi_to_dsi_timing(tmp, bpp,
 						 DSI_HSA_FRAME_OVERHEAD);
-		dsi_htotal += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
-		dsi_hss_hsa_hse_hbp += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
 	}
 
 	dsi_cfg->hact = dpi_to_dsi_timing(mode_valid_check ?
 					  mode->hdisplay : mode->crtc_hdisplay,
 					  bpp, 0);
-	dsi_htotal += dsi_cfg->hact;
+	dsi_cfg->hfp = dpi_to_dsi_timing(mode_to_dpi_hfp(mode), bpp,
+					 DSI_HFP_FRAME_OVERHEAD);
 
-	if (mode_valid_check)
-		dpi_hfp = mode->hsync_start - mode->hdisplay;
-	else
-		dpi_hfp = mode->crtc_hsync_start - mode->crtc_hdisplay;
+	return 0;
+}
+
+static int cdns_dphy_validate(struct cdns_dsi *dsi,
+			      struct cdns_dsi_cfg *dsi_cfg,
+			      struct cdns_dphy_cfg *dphy_cfg,
+			      const struct drm_display_mode *mode,
+			      bool mode_valid_check)
+{
+	struct cdns_dsi_output *output = &dsi->output;
+	unsigned long dsi_htotal;
+	unsigned int dsi_hfp_ext = 0;
+
+	int ret;
 
-	dsi_cfg->hfp = dpi_to_dsi_timing(dpi_hfp, bpp, DSI_HFP_FRAME_OVERHEAD);
+	dsi_htotal = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
+	if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+		dsi_htotal += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
+
+	dsi_htotal += dsi_cfg->hact;
 	dsi_htotal += dsi_cfg->hfp + DSI_HFP_FRAME_OVERHEAD;
 
 	if (mode_valid_check)
 		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
-						mode->htotal, bpp,
+						mode->htotal,
 						mode->clock * 1000,
-						dsi_htotal, nlanes,
+						mipi_dsi_pixel_format_to_bpp(output->dev->format),
+						dsi_htotal,
+						output->dev->lanes,
 						&dsi_hfp_ext);
 	else
 		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
-						mode->crtc_htotal, bpp,
+						mode->crtc_htotal,
+						mipi_dsi_pixel_format_to_bpp(output->dev->format),
 						mode->crtc_clock * 1000,
-						dsi_htotal, nlanes,
+						dsi_htotal,
+						output->dev->lanes,
 						&dsi_hfp_ext);
-
 	if (ret)
 		return ret;
 
 	dsi_cfg->hfp += dsi_hfp_ext;
-	dsi_htotal += dsi_hfp_ext;
-	dsi_cfg->htotal = dsi_htotal;
+	dsi_cfg->htotal = dsi_htotal + dsi_hfp_ext;
+
+	return 0;
+}
+
+static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
+			       const struct drm_display_mode *mode,
+			       struct cdns_dsi_cfg *dsi_cfg,
+			       struct cdns_dphy_cfg *dphy_cfg,
+			       bool mode_valid_check)
+{
+	struct cdns_dsi_output *output = &dsi->output;
+	unsigned long dsi_hss_hsa_hse_hbp;
+	unsigned int nlanes = output->dev->lanes;
+	int ret;
+
+	ret = cdns_dsi_mode2cfg(dsi, mode, dsi_cfg, mode_valid_check);
+	if (ret)
+		return ret;
+
+	ret = cdns_dphy_validate(dsi, dsi_cfg, dphy_cfg, mode, mode_valid_check);
+	if (ret)
+		return ret;
+
+	dsi_hss_hsa_hse_hbp = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
+	if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+		dsi_hss_hsa_hse_hbp += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
 
 	/*
 	 * Make sure DPI(HFP) > DSI(HSS+HSA+HSE+HBP) to guarantee that the FIFO
 	 * is empty before we start a receiving a new line on the DPI
 	 * interface.
 	 */
-	if ((u64)dphy_cfg->lane_bps * dpi_hfp * nlanes <
+	if ((u64)dphy_cfg->lane_bps * mode_to_dpi_hfp(mode) * nlanes <
 	    (u64)dsi_hss_hsa_hse_hbp *
 	    (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000)
 		return -EINVAL;
@@ -842,7 +884,7 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
 	struct cdns_dsi_output *output = &dsi->output;
 	struct cdns_dphy_cfg dphy_cfg;
 	struct cdns_dsi_cfg dsi_cfg;
-	int bpp, nlanes, ret;
+	int bpp, ret;
 
 	/*
 	 * VFP_DSI should be less than VFP_DPI and VFP_DSI should be at
@@ -860,11 +902,9 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
 	if ((mode->hdisplay * bpp) % 32)
 		return MODE_H_ILLEGAL;
 
-	nlanes = output->dev->lanes;
-
-	ret = cdns_dsi_mode2cfg(dsi, mode, &dsi_cfg, &dphy_cfg, true);
+	ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg, true);
 	if (ret)
-		return MODE_CLOCK_RANGE;
+		return MODE_BAD;
 
 	return MODE_OK;
 }
@@ -990,7 +1030,7 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
 	bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format);
 	nlanes = output->dev->lanes;
 
-	WARN_ON_ONCE(cdns_dsi_mode2cfg(dsi, mode, &dsi_cfg, &dphy_cfg, false));
+	WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg, false));
 
 	cdns_dsi_hs_init(dsi, &dphy_cfg);
 	cdns_dsi_init_link(dsi);
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 7/9] dt-bindings: phy: Move the Cadence D-PHY bindings
  2019-01-09  9:33 [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (5 preceding siblings ...)
  2019-01-09  9:33 ` [PATCH v4 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
@ 2019-01-09  9:33 ` Maxime Ripard
  2019-01-09 12:39   ` Sakari Ailus
  2019-01-09  9:33 ` [PATCH v4 8/9] phy: Add Cadence D-PHY support Maxime Ripard
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Maxime Ripard @ 2019-01-09  9:33 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

The Cadence D-PHY bindings was defined as part of the DSI block so far.
However, since it's now going to be a separate driver, we need to move the
binding to a file of its own.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt | 21 +-------
 Documentation/devicetree/bindings/phy/cdns,dphy.txt           | 20 +++++++-
 2 files changed, 20 insertions(+), 21 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/phy/cdns,dphy.txt

diff --git a/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt b/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
index f5725bb6c61c..525a4bfd8634 100644
--- a/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
+++ b/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
@@ -31,28 +31,7 @@ Required subnodes:
 - one subnode per DSI device connected on the DSI bus. Each DSI device should
   contain a reg property encoding its virtual channel.
 
-Cadence DPHY
-============
-
-Cadence DPHY block.
-
-Required properties:
-- compatible: should be set to "cdns,dphy".
-- reg: physical base address and length of the DPHY registers.
-- clocks: DPHY reference clocks.
-- clock-names: must contain "psm" and "pll_ref".
-- #phy-cells: must be set to 0.
-
-
 Example:
-	dphy0: dphy@fd0e0000{
-		compatible = "cdns,dphy";
-		reg = <0x0 0xfd0e0000 0x0 0x1000>;
-		clocks = <&psm_clk>, <&pll_ref_clk>;
-		clock-names = "psm", "pll_ref";
-		#phy-cells = <0>;
-	};
-
 	dsi0: dsi@fd0c0000 {
 		compatible = "cdns,dsi";
 		reg = <0x0 0xfd0c0000 0x0 0x1000>;
diff --git a/Documentation/devicetree/bindings/phy/cdns,dphy.txt b/Documentation/devicetree/bindings/phy/cdns,dphy.txt
new file mode 100644
index 000000000000..1095bc4e72d9
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/cdns,dphy.txt
@@ -0,0 +1,20 @@
+Cadence DPHY
+============
+
+Cadence DPHY block.
+
+Required properties:
+- compatible: should be set to "cdns,dphy".
+- reg: physical base address and length of the DPHY registers.
+- clocks: DPHY reference clocks.
+- clock-names: must contain "psm" and "pll_ref".
+- #phy-cells: must be set to 0.
+
+Example:
+	dphy0: dphy@fd0e0000{
+		compatible = "cdns,dphy";
+		reg = <0x0 0xfd0e0000 0x0 0x1000>;
+		clocks = <&psm_clk>, <&pll_ref_clk>;
+		clock-names = "psm", "pll_ref";
+		#phy-cells = <0>;
+	};
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 8/9] phy: Add Cadence D-PHY support
  2019-01-09  9:33 [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (6 preceding siblings ...)
  2019-01-09  9:33 ` [PATCH v4 7/9] dt-bindings: phy: Move the Cadence D-PHY bindings Maxime Ripard
@ 2019-01-09  9:33 ` Maxime Ripard
  2019-01-09 12:41   ` Sakari Ailus
  2019-01-17 13:53   ` Sean Paul
  2019-01-09  9:33 ` [PATCH v4 9/9] drm/bridge: cdns: Convert to phy framework Maxime Ripard
  2019-01-09 12:28 ` [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Sakari Ailus
  9 siblings, 2 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-09  9:33 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

Cadence has designed a D-PHY that can be used by the, currently in tree,
DSI bridge (DRM), CSI Transceiver and CSI Receiver (v4l2) drivers.

Only the DSI driver has an ad-hoc driver for that phy at the moment, while
the v4l2 drivers are completely missing any phy support. In order to make
that phy support available to all these drivers, without having to
duplicate that code three times, let's create a generic phy framework
driver.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/phy/cadence/Kconfig     |  13 +-
 drivers/phy/cadence/Makefile    |   1 +-
 drivers/phy/cadence/cdns-dphy.c | 389 +++++++++++++++++++++++++++++++++-
 3 files changed, 402 insertions(+), 1 deletion(-)
 create mode 100644 drivers/phy/cadence/cdns-dphy.c

diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
index 2b8c0851ff33..31f18b67dd7c 100644
--- a/drivers/phy/cadence/Kconfig
+++ b/drivers/phy/cadence/Kconfig
@@ -1,6 +1,7 @@
 #
 # Phy drivers for Cadence PHYs
 #
+
 config PHY_CADENCE_DP
 	tristate "Cadence MHDP DisplayPort PHY driver"
 	depends on OF
@@ -9,9 +10,19 @@ config PHY_CADENCE_DP
 	help
 	  Support for Cadence MHDP DisplayPort PHY.
 
+config PHY_CADENCE_DPHY
+	tristate "Cadence D-PHY Support"
+	depends on HAS_IOMEM && OF
+	select GENERIC_PHY
+	select GENERIC_PHY_MIPI_DPHY
+	help
+	  Choose this option if you have a Cadence D-PHY in your
+	  system. If M is selected, the module will be called
+	  cdns-dphy.
+
 config PHY_CADENCE_SIERRA
 	tristate "Cadence Sierra PHY Driver"
 	depends on OF && HAS_IOMEM && RESET_CONTROLLER
 	select GENERIC_PHY
 	help
-	  Enable this to support the Cadence Sierra PHY driver
\ No newline at end of file
+	  Enable this to support the Cadence Sierra PHY driver
diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile
index 412349af0492..2f9e3457b954 100644
--- a/drivers/phy/cadence/Makefile
+++ b/drivers/phy/cadence/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_PHY_CADENCE_DP)	+= phy-cadence-dp.o
+obj-$(CONFIG_PHY_CADENCE_DPHY)	+= cdns-dphy.o
 obj-$(CONFIG_PHY_CADENCE_SIERRA)	+= phy-cadence-sierra.o
diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c
new file mode 100644
index 000000000000..1d0abba03f37
--- /dev/null
+++ b/drivers/phy/cadence/cdns-dphy.c
@@ -0,0 +1,389 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright: 2017-2018 Cadence Design Systems, Inc.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+
+#define REG_WAKEUP_TIME_NS		800
+#define DPHY_PLL_RATE_HZ		108000000
+
+/* DPHY registers */
+#define DPHY_PMA_CMN(reg)		(reg)
+#define DPHY_PMA_LCLK(reg)		(0x100 + (reg))
+#define DPHY_PMA_LDATA(lane, reg)	(0x200 + ((lane) * 0x100) + (reg))
+#define DPHY_PMA_RCLK(reg)		(0x600 + (reg))
+#define DPHY_PMA_RDATA(lane, reg)	(0x700 + ((lane) * 0x100) + (reg))
+#define DPHY_PCS(reg)			(0xb00 + (reg))
+
+#define DPHY_CMN_SSM			DPHY_PMA_CMN(0x20)
+#define DPHY_CMN_SSM_EN			BIT(0)
+#define DPHY_CMN_TX_MODE_EN		BIT(9)
+
+#define DPHY_CMN_PWM			DPHY_PMA_CMN(0x40)
+#define DPHY_CMN_PWM_DIV(x)		((x) << 20)
+#define DPHY_CMN_PWM_LOW(x)		((x) << 10)
+#define DPHY_CMN_PWM_HIGH(x)		(x)
+
+#define DPHY_CMN_FBDIV			DPHY_PMA_CMN(0x4c)
+#define DPHY_CMN_FBDIV_VAL(low, high)	(((high) << 11) | ((low) << 22))
+#define DPHY_CMN_FBDIV_FROM_REG		(BIT(10) | BIT(21))
+
+#define DPHY_CMN_OPIPDIV		DPHY_PMA_CMN(0x50)
+#define DPHY_CMN_IPDIV_FROM_REG		BIT(0)
+#define DPHY_CMN_IPDIV(x)		((x) << 1)
+#define DPHY_CMN_OPDIV_FROM_REG		BIT(6)
+#define DPHY_CMN_OPDIV(x)		((x) << 7)
+
+#define DPHY_PSM_CFG			DPHY_PCS(0x4)
+#define DPHY_PSM_CFG_FROM_REG		BIT(0)
+#define DPHY_PSM_CLK_DIV(x)		((x) << 1)
+
+#define DSI_HBP_FRAME_OVERHEAD		12
+#define DSI_HSA_FRAME_OVERHEAD		14
+#define DSI_HFP_FRAME_OVERHEAD		6
+#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD	4
+#define DSI_BLANKING_FRAME_OVERHEAD	6
+#define DSI_NULL_FRAME_OVERHEAD		6
+#define DSI_EOT_PKT_SIZE		4
+
+struct cdns_dphy_cfg {
+	u8 pll_ipdiv;
+	u8 pll_opdiv;
+	u16 pll_fbdiv;
+	unsigned int nlanes;
+};
+
+enum cdns_dphy_clk_lane_cfg {
+	DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
+	DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
+	DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
+	DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
+};
+
+struct cdns_dphy;
+struct cdns_dphy_ops {
+	int (*probe)(struct cdns_dphy *dphy);
+	void (*remove)(struct cdns_dphy *dphy);
+	void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
+	void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
+				 enum cdns_dphy_clk_lane_cfg cfg);
+	void (*set_pll_cfg)(struct cdns_dphy *dphy,
+			    const struct cdns_dphy_cfg *cfg);
+	unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
+};
+
+struct cdns_dphy {
+	struct cdns_dphy_cfg cfg;
+	void __iomem *regs;
+	struct clk *psm_clk;
+	struct clk *pll_ref_clk;
+	const struct cdns_dphy_ops *ops;
+	struct phy *phy;
+};
+
+static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
+				     struct cdns_dphy_cfg *cfg,
+				     struct phy_configure_opts_mipi_dphy *opts,
+				     unsigned int *dsi_hfp_ext)
+{
+	unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk);
+	u64 dlane_bps;
+
+	memset(cfg, 0, sizeof(*cfg));
+
+	if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
+		return -EINVAL;
+	else if (pll_ref_hz < 19200000)
+		cfg->pll_ipdiv = 1;
+	else if (pll_ref_hz < 38400000)
+		cfg->pll_ipdiv = 2;
+	else if (pll_ref_hz < 76800000)
+		cfg->pll_ipdiv = 4;
+	else
+		cfg->pll_ipdiv = 8;
+
+	dlane_bps = opts->hs_clk_rate;
+
+	if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
+		return -EINVAL;
+	else if (dlane_bps >= 1250000000)
+		cfg->pll_opdiv = 1;
+	else if (dlane_bps >= 630000000)
+		cfg->pll_opdiv = 2;
+	else if (dlane_bps >= 320000000)
+		cfg->pll_opdiv = 4;
+	else if (dlane_bps >= 160000000)
+		cfg->pll_opdiv = 8;
+
+	cfg->pll_fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv *
+					  cfg->pll_ipdiv,
+					  pll_ref_hz);
+
+	return 0;
+}
+
+static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
+{
+	unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
+	unsigned long psm_div;
+
+	if (!psm_clk_hz || psm_clk_hz > 100000000)
+		return -EINVAL;
+
+	psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
+	if (dphy->ops->set_psm_div)
+		dphy->ops->set_psm_div(dphy, psm_div);
+
+	return 0;
+}
+
+static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
+				       enum cdns_dphy_clk_lane_cfg cfg)
+{
+	if (dphy->ops->set_clk_lane_cfg)
+		dphy->ops->set_clk_lane_cfg(dphy, cfg);
+}
+
+static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
+				  const struct cdns_dphy_cfg *cfg)
+{
+	if (dphy->ops->set_pll_cfg)
+		dphy->ops->set_pll_cfg(dphy, cfg);
+}
+
+static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
+{
+	return dphy->ops->get_wakeup_time_ns(dphy);
+}
+
+static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
+{
+	/* Default wakeup time is 800 ns (in a simulated environment). */
+	return 800;
+}
+
+static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
+				      const struct cdns_dphy_cfg *cfg)
+{
+	u32 fbdiv_low, fbdiv_high;
+
+	fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
+	fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
+
+	writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
+	       DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
+	       DPHY_CMN_OPDIV(cfg->pll_opdiv),
+	       dphy->regs + DPHY_CMN_OPIPDIV);
+	writel(DPHY_CMN_FBDIV_FROM_REG |
+	       DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
+	       dphy->regs + DPHY_CMN_FBDIV);
+	writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
+	       DPHY_CMN_PWM_DIV(0x8),
+	       dphy->regs + DPHY_CMN_PWM);
+}
+
+static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
+{
+	writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
+	       dphy->regs + DPHY_PSM_CFG);
+}
+
+/*
+ * This is the reference implementation of DPHY hooks. Specific integration of
+ * this IP may have to re-implement some of them depending on how they decided
+ * to wire things in the SoC.
+ */
+static const struct cdns_dphy_ops ref_dphy_ops = {
+	.get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
+	.set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
+	.set_psm_div = cdns_dphy_ref_set_psm_div,
+};
+
+static int cdns_dphy_config_from_opts(struct phy *phy,
+				      struct phy_configure_opts_mipi_dphy *opts,
+				      struct cdns_dphy_cfg *cfg)
+{
+	struct cdns_dphy *dphy = phy_get_drvdata(phy);
+	unsigned int dsi_hfp_ext = 0;
+	int ret;
+
+	ret = phy_mipi_dphy_config_validate(opts);
+	if (ret)
+		return ret;
+
+	ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg,
+					opts, &dsi_hfp_ext);
+	if (ret)
+		return ret;
+
+	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) * 1000;
+
+	return 0;
+}
+
+static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
+			      union phy_configure_opts *opts)
+{
+	struct cdns_dphy_cfg cfg = { 0 };
+
+	if (mode != PHY_MODE_MIPI_DPHY)
+		return -EINVAL;
+
+	return cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
+}
+
+static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+	struct cdns_dphy *dphy = phy_get_drvdata(phy);
+	struct cdns_dphy_cfg cfg = { 0 };
+	int ret;
+
+	ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
+	if (ret)
+		return ret;
+
+	/*
+	 * Configure the internal PSM clk divider so that the DPHY has a
+	 * 1MHz clk (or something close).
+	 */
+	ret = cdns_dphy_setup_psm(dphy);
+	if (ret)
+		return ret;
+
+	/*
+	 * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
+	 * and 8 data lanes, each clk lane can be attache different set of
+	 * data lanes. The 2 groups are named 'left' and 'right', so here we
+	 * just say that we want the 'left' clk lane to drive the 'left' data
+	 * lanes.
+	 */
+	cdns_dphy_set_clk_lane_cfg(dphy, DPHY_CLK_CFG_LEFT_DRIVES_LEFT);
+
+	/*
+	 * Configure the DPHY PLL that will be used to generate the TX byte
+	 * clk.
+	 */
+	cdns_dphy_set_pll_cfg(dphy, &cfg);
+
+	return 0;
+}
+
+static int cdns_dphy_power_on(struct phy *phy)
+{
+	struct cdns_dphy *dphy = phy_get_drvdata(phy);
+
+	clk_prepare_enable(dphy->psm_clk);
+	clk_prepare_enable(dphy->pll_ref_clk);
+
+	/* Start TX state machine. */
+	writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN, dphy->regs + DPHY_CMN_SSM);
+
+	return 0;
+}
+
+static int cdns_dphy_power_off(struct phy *phy)
+{
+	struct cdns_dphy *dphy = phy_get_drvdata(phy);
+
+	clk_disable_unprepare(dphy->pll_ref_clk);
+	clk_disable_unprepare(dphy->psm_clk);
+
+	return 0;
+}
+
+static const struct phy_ops cdns_dphy_ops = {
+	.configure	= cdns_dphy_configure,
+	.validate	= cdns_dphy_validate,
+	.power_on	= cdns_dphy_power_on,
+	.power_off	= cdns_dphy_power_off,
+};
+
+static int cdns_dphy_probe(struct platform_device *pdev)
+{
+	struct phy_provider *phy_provider;
+	struct cdns_dphy *dphy;
+	struct resource *res;
+	int ret;
+
+	dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
+	if (!dphy)
+		return -ENOMEM;
+	dev_set_drvdata(&pdev->dev, dphy);
+
+	dphy->ops = of_device_get_match_data(&pdev->dev);
+	if (!dphy->ops)
+		return -EINVAL;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dphy->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(dphy->regs))
+		return PTR_ERR(dphy->regs);
+
+	dphy->psm_clk = devm_clk_get(&pdev->dev, "psm");
+	if (IS_ERR(dphy->psm_clk))
+		return PTR_ERR(dphy->psm_clk);
+
+	dphy->pll_ref_clk = devm_clk_get(&pdev->dev, "pll_ref");
+	if (IS_ERR(dphy->pll_ref_clk))
+		return PTR_ERR(dphy->pll_ref_clk);
+
+	if (dphy->ops->probe) {
+		ret = dphy->ops->probe(dphy);
+		if (ret)
+			return ret;
+	}
+
+	dphy->phy = devm_phy_create(&pdev->dev, NULL, &cdns_dphy_ops);
+	if (IS_ERR(dphy->phy)) {
+		dev_err(&pdev->dev, "failed to create PHY\n");
+		if (dphy->ops->remove)
+			dphy->ops->remove(dphy);
+		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 int cdns_dphy_remove(struct platform_device *pdev)
+{
+	struct cdns_dphy *dphy = dev_get_drvdata(&pdev->dev);
+
+	if (dphy->ops->remove)
+		dphy->ops->remove(dphy);
+
+	return 0;
+}
+
+static const struct of_device_id cdns_dphy_of_match[] = {
+	{ .compatible = "cdns,dphy", .data = &ref_dphy_ops },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, cdns_dphy_of_match);
+
+static struct platform_driver cdns_dphy_platform_driver = {
+	.probe		= cdns_dphy_probe,
+	.remove		= cdns_dphy_remove,
+	.driver		= {
+		.name		= "cdns-mipi-dphy",
+		.of_match_table	= cdns_dphy_of_match,
+	},
+};
+module_platform_driver(cdns_dphy_platform_driver);
+
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
+MODULE_DESCRIPTION("Cadence MIPI D-PHY Driver");
+MODULE_LICENSE("GPL");
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH v4 9/9] drm/bridge: cdns: Convert to phy framework
  2019-01-09  9:33 [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (7 preceding siblings ...)
  2019-01-09  9:33 ` [PATCH v4 8/9] phy: Add Cadence D-PHY support Maxime Ripard
@ 2019-01-09  9:33 ` Maxime Ripard
  2019-01-16  8:37   ` Kishon Vijay Abraham I
  2019-01-17 13:53   ` Sean Paul
  2019-01-09 12:28 ` [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Sakari Ailus
  9 siblings, 2 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-09  9:33 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

Now that we have everything we need in the phy framework to allow to tune
the phy parameters, let's convert the Cadence DSI bridge to that API
instead of creating a ad-hoc driver for its phy.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/gpu/drm/bridge/Kconfig    |   1 +-
 drivers/gpu/drm/bridge/cdns-dsi.c | 485 +++----------------------------
 drivers/phy/cadence/cdns-dphy.c   |   2 +-
 3 files changed, 61 insertions(+), 427 deletions(-)

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 2fee47b0d50b..8840f396a7b6 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -30,6 +30,7 @@ config DRM_CDNS_DSI
 	select DRM_KMS_HELPER
 	select DRM_MIPI_DSI
 	select DRM_PANEL_BRIDGE
+	select GENERIC_PHY_MIPI_DPHY
 	depends on OF
 	help
 	  Support Cadence DPI to DSI bridge. This is an internal
diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
index 3ac6dd524b6d..7b432257ffbe 100644
--- a/drivers/gpu/drm/bridge/cdns-dsi.c
+++ b/drivers/gpu/drm/bridge/cdns-dsi.c
@@ -21,6 +21,9 @@
 #include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+
 #define IP_CONF				0x0
 #define SP_HS_FIFO_DEPTH(x)		(((x) & GENMASK(30, 26)) >> 26)
 #define SP_LP_FIFO_DEPTH(x)		(((x) & GENMASK(25, 21)) >> 21)
@@ -419,44 +422,11 @@
 #define DSI_NULL_FRAME_OVERHEAD		6
 #define DSI_EOT_PKT_SIZE		4
 
-#define REG_WAKEUP_TIME_NS		800
-#define DPHY_PLL_RATE_HZ		108000000
-
-/* DPHY registers */
-#define DPHY_PMA_CMN(reg)		(reg)
-#define DPHY_PMA_LCLK(reg)		(0x100 + (reg))
-#define DPHY_PMA_LDATA(lane, reg)	(0x200 + ((lane) * 0x100) + (reg))
-#define DPHY_PMA_RCLK(reg)		(0x600 + (reg))
-#define DPHY_PMA_RDATA(lane, reg)	(0x700 + ((lane) * 0x100) + (reg))
-#define DPHY_PCS(reg)			(0xb00 + (reg))
-
-#define DPHY_CMN_SSM			DPHY_PMA_CMN(0x20)
-#define DPHY_CMN_SSM_EN			BIT(0)
-#define DPHY_CMN_TX_MODE_EN		BIT(9)
-
-#define DPHY_CMN_PWM			DPHY_PMA_CMN(0x40)
-#define DPHY_CMN_PWM_DIV(x)		((x) << 20)
-#define DPHY_CMN_PWM_LOW(x)		((x) << 10)
-#define DPHY_CMN_PWM_HIGH(x)		(x)
-
-#define DPHY_CMN_FBDIV			DPHY_PMA_CMN(0x4c)
-#define DPHY_CMN_FBDIV_VAL(low, high)	(((high) << 11) | ((low) << 22))
-#define DPHY_CMN_FBDIV_FROM_REG		(BIT(10) | BIT(21))
-
-#define DPHY_CMN_OPIPDIV		DPHY_PMA_CMN(0x50)
-#define DPHY_CMN_IPDIV_FROM_REG		BIT(0)
-#define DPHY_CMN_IPDIV(x)		((x) << 1)
-#define DPHY_CMN_OPDIV_FROM_REG		BIT(6)
-#define DPHY_CMN_OPDIV(x)		((x) << 7)
-
-#define DPHY_PSM_CFG			DPHY_PCS(0x4)
-#define DPHY_PSM_CFG_FROM_REG		BIT(0)
-#define DPHY_PSM_CLK_DIV(x)		((x) << 1)
-
 struct cdns_dsi_output {
 	struct mipi_dsi_device *dev;
 	struct drm_panel *panel;
 	struct drm_bridge *bridge;
+	union phy_configure_opts phy_opts;
 };
 
 enum cdns_dsi_input_id {
@@ -465,14 +435,6 @@ enum cdns_dsi_input_id {
 	CDNS_DSC_INPUT,
 };
 
-struct cdns_dphy_cfg {
-	u8 pll_ipdiv;
-	u8 pll_opdiv;
-	u16 pll_fbdiv;
-	unsigned long lane_bps;
-	unsigned int nlanes;
-};
-
 struct cdns_dsi_cfg {
 	unsigned int hfp;
 	unsigned int hsa;
@@ -481,34 +443,6 @@ struct cdns_dsi_cfg {
 	unsigned int htotal;
 };
 
-struct cdns_dphy;
-
-enum cdns_dphy_clk_lane_cfg {
-	DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
-	DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
-	DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
-	DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
-};
-
-struct cdns_dphy_ops {
-	int (*probe)(struct cdns_dphy *dphy);
-	void (*remove)(struct cdns_dphy *dphy);
-	void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
-	void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
-				 enum cdns_dphy_clk_lane_cfg cfg);
-	void (*set_pll_cfg)(struct cdns_dphy *dphy,
-			    const struct cdns_dphy_cfg *cfg);
-	unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
-};
-
-struct cdns_dphy {
-	struct cdns_dphy_cfg cfg;
-	void __iomem *regs;
-	struct clk *psm_clk;
-	struct clk *pll_ref_clk;
-	const struct cdns_dphy_ops *ops;
-};
-
 struct cdns_dsi_input {
 	enum cdns_dsi_input_id id;
 	struct drm_bridge bridge;
@@ -526,7 +460,7 @@ struct cdns_dsi {
 	struct reset_control *dsi_p_rst;
 	struct clk *dsi_sys_clk;
 	bool link_initialized;
-	struct cdns_dphy *dphy;
+	struct phy *dphy;
 };
 
 static inline struct cdns_dsi *input_to_dsi(struct cdns_dsi_input *input)
@@ -550,175 +484,6 @@ static unsigned int mode_to_dpi_hfp(const struct drm_display_mode *mode)
 	return mode->crtc_hsync_start - mode->crtc_hdisplay;
 }
 
-static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
-				     struct cdns_dphy_cfg *cfg,
-				     unsigned int dpi_htotal,
-				     unsigned int dpi_bpp,
-				     unsigned int dpi_hz,
-				     unsigned int dsi_htotal,
-				     unsigned int dsi_nlanes,
-				     unsigned int *dsi_hfp_ext)
-{
-	u64 dlane_bps, dlane_bps_max, fbdiv, fbdiv_max, adj_dsi_htotal;
-	unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk);
-
-	memset(cfg, 0, sizeof(*cfg));
-
-	cfg->nlanes = dsi_nlanes;
-
-	if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
-		return -EINVAL;
-	else if (pll_ref_hz < 19200000)
-		cfg->pll_ipdiv = 1;
-	else if (pll_ref_hz < 38400000)
-		cfg->pll_ipdiv = 2;
-	else if (pll_ref_hz < 76800000)
-		cfg->pll_ipdiv = 4;
-	else
-		cfg->pll_ipdiv = 8;
-
-	/*
-	 * Make sure DSI htotal is aligned on a lane boundary when calculating
-	 * the expected data rate. This is done by extending HFP in case of
-	 * misalignment.
-	 */
-	adj_dsi_htotal = dsi_htotal;
-	if (dsi_htotal % dsi_nlanes)
-		adj_dsi_htotal += dsi_nlanes - (dsi_htotal % dsi_nlanes);
-
-	dlane_bps = (u64)dpi_hz * adj_dsi_htotal;
-
-	/* data rate in bytes/sec is not an integer, refuse the mode. */
-	if (do_div(dlane_bps, dsi_nlanes * dpi_htotal))
-		return -EINVAL;
-
-	/* data rate was in bytes/sec, convert to bits/sec. */
-	dlane_bps *= 8;
-
-	if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
-		return -EINVAL;
-	else if (dlane_bps >= 1250000000)
-		cfg->pll_opdiv = 1;
-	else if (dlane_bps >= 630000000)
-		cfg->pll_opdiv = 2;
-	else if (dlane_bps >= 320000000)
-		cfg->pll_opdiv = 4;
-	else if (dlane_bps >= 160000000)
-		cfg->pll_opdiv = 8;
-
-	/*
-	 * Allow a deviation of 0.2% on the per-lane data rate to try to
-	 * recover a potential mismatch between DPI and PPI clks.
-	 */
-	dlane_bps_max = dlane_bps + DIV_ROUND_DOWN_ULL(dlane_bps, 500);
-	fbdiv_max = DIV_ROUND_DOWN_ULL(dlane_bps_max * 2 *
-				       cfg->pll_opdiv * cfg->pll_ipdiv,
-				       pll_ref_hz);
-	fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv *
-				 cfg->pll_ipdiv,
-				 pll_ref_hz);
-
-	/*
-	 * Iterate over all acceptable fbdiv and try to find an adjusted DSI
-	 * htotal length providing an exact match.
-	 *
-	 * Note that we could do something even trickier by relying on the fact
-	 * that a new line is not necessarily aligned on a lane boundary, so,
-	 * by making adj_dsi_htotal non aligned on a dsi_lanes we can improve a
-	 * bit the precision. With this, the step would be
-	 *
-	 *	pll_ref_hz / (2 * opdiv * ipdiv * nlanes)
-	 *
-	 * instead of
-	 *
-	 *	pll_ref_hz / (2 * opdiv * ipdiv)
-	 *
-	 * The drawback of this approach is that we would need to make sure the
-	 * number or lines is a multiple of the realignment periodicity which is
-	 * a function of the number of lanes and the original misalignment. For
-	 * example, for NLANES = 4 and HTOTAL % NLANES = 3, it takes 4 lines
-	 * to realign on a lane:
-	 * LINE 0: expected number of bytes, starts emitting first byte of
-	 *	   LINE 1 on LANE 3
-	 * LINE 1: expected number of bytes, starts emitting first 2 bytes of
-	 *	   LINE 2 on LANES 2 and 3
-	 * LINE 2: expected number of bytes, starts emitting first 3 bytes of
-	 *	   of LINE 3 on LANES 1, 2 and 3
-	 * LINE 3: one byte less, now things are realigned on LANE 0 for LINE 4
-	 *
-	 * I figured this extra complexity was not worth the benefit, but if
-	 * someone really has unfixable mismatch, that would be something to
-	 * investigate.
-	 */
-	for (; fbdiv <= fbdiv_max; fbdiv++) {
-		u32 rem;
-
-		adj_dsi_htotal = (u64)fbdiv * pll_ref_hz * dsi_nlanes *
-				 dpi_htotal;
-
-		/*
-		 * Do the division in 2 steps to avoid an overflow on the
-		 * divider.
-		 */
-		rem = do_div(adj_dsi_htotal, dpi_hz);
-		if (rem)
-			continue;
-
-		rem = do_div(adj_dsi_htotal,
-			     cfg->pll_opdiv * cfg->pll_ipdiv * 2 * 8);
-		if (rem)
-			continue;
-
-		cfg->pll_fbdiv = fbdiv;
-		*dsi_hfp_ext = adj_dsi_htotal - dsi_htotal;
-		break;
-	}
-
-	/* No match, let's just reject the display mode. */
-	if (!cfg->pll_fbdiv)
-		return -EINVAL;
-
-	dlane_bps = DIV_ROUND_DOWN_ULL((u64)dpi_hz * adj_dsi_htotal * 8,
-				       dsi_nlanes * dpi_htotal);
-	cfg->lane_bps = dlane_bps;
-
-	return 0;
-}
-
-static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
-{
-	unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
-	unsigned long psm_div;
-
-	if (!psm_clk_hz || psm_clk_hz > 100000000)
-		return -EINVAL;
-
-	psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
-	if (dphy->ops->set_psm_div)
-		dphy->ops->set_psm_div(dphy, psm_div);
-
-	return 0;
-}
-
-static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
-				       enum cdns_dphy_clk_lane_cfg cfg)
-{
-	if (dphy->ops->set_clk_lane_cfg)
-		dphy->ops->set_clk_lane_cfg(dphy, cfg);
-}
-
-static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
-				  const struct cdns_dphy_cfg *cfg)
-{
-	if (dphy->ops->set_pll_cfg)
-		dphy->ops->set_pll_cfg(dphy, cfg);
-}
-
-static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
-{
-	return dphy->ops->get_wakeup_time_ns(dphy);
-}
-
 static unsigned int dpi_to_dsi_timing(unsigned int dpi_timing,
 				      unsigned int dpi_bpp,
 				      unsigned int dsi_pkt_overhead)
@@ -780,17 +545,20 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
 	return 0;
 }
 
-static int cdns_dphy_validate(struct cdns_dsi *dsi,
+static int cdns_dsi_adjust_phy_config(struct cdns_dsi *dsi,
 			      struct cdns_dsi_cfg *dsi_cfg,
-			      struct cdns_dphy_cfg *dphy_cfg,
+			      struct phy_configure_opts_mipi_dphy *phy_cfg,
 			      const struct drm_display_mode *mode,
 			      bool mode_valid_check)
 {
 	struct cdns_dsi_output *output = &dsi->output;
+	unsigned long long dlane_bps;
+	unsigned long adj_dsi_htotal;
 	unsigned long dsi_htotal;
-	unsigned int dsi_hfp_ext = 0;
-
-	int ret;
+	unsigned long dpi_htotal;
+	unsigned long dpi_hz;
+	unsigned int dsi_hfp_ext;
+	unsigned int lanes = output->dev->lanes;
 
 	dsi_htotal = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
 	if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
@@ -799,25 +567,27 @@ static int cdns_dphy_validate(struct cdns_dsi *dsi,
 	dsi_htotal += dsi_cfg->hact;
 	dsi_htotal += dsi_cfg->hfp + DSI_HFP_FRAME_OVERHEAD;
 
-	if (mode_valid_check)
-		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
-						mode->htotal,
-						mode->clock * 1000,
-						mipi_dsi_pixel_format_to_bpp(output->dev->format),
-						dsi_htotal,
-						output->dev->lanes,
-						&dsi_hfp_ext);
-	else
-		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
-						mode->crtc_htotal,
-						mipi_dsi_pixel_format_to_bpp(output->dev->format),
-						mode->crtc_clock * 1000,
-						dsi_htotal,
-						output->dev->lanes,
-						&dsi_hfp_ext);
-	if (ret)
-		return ret;
+	/*
+	 * Make sure DSI htotal is aligned on a lane boundary when calculating
+	 * the expected data rate. This is done by extending HFP in case of
+	 * misalignment.
+	 */
+	adj_dsi_htotal = dsi_htotal;
+	if (dsi_htotal % lanes)
+		adj_dsi_htotal += lanes - (dsi_htotal % lanes);
+
+	dpi_hz = (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000;
+	dlane_bps = (unsigned long long)dpi_hz * adj_dsi_htotal;
+
+	/* data rate in bytes/sec is not an integer, refuse the mode. */
+	dpi_htotal = mode_valid_check ? mode->htotal : mode->crtc_htotal;
+	if (do_div(dlane_bps, lanes * dpi_htotal))
+		return -EINVAL;
 
+	/* data rate was in bytes/sec, convert to bits/sec. */
+	phy_cfg->hs_clk_rate = dlane_bps * 8;
+
+	dsi_hfp_ext = adj_dsi_htotal - dsi_htotal;
 	dsi_cfg->hfp += dsi_hfp_ext;
 	dsi_cfg->htotal = dsi_htotal + dsi_hfp_ext;
 
@@ -827,10 +597,10 @@ static int cdns_dphy_validate(struct cdns_dsi *dsi,
 static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
 			       const struct drm_display_mode *mode,
 			       struct cdns_dsi_cfg *dsi_cfg,
-			       struct cdns_dphy_cfg *dphy_cfg,
 			       bool mode_valid_check)
 {
 	struct cdns_dsi_output *output = &dsi->output;
+	struct phy_configure_opts_mipi_dphy *phy_cfg = &output->phy_opts.mipi_dphy;
 	unsigned long dsi_hss_hsa_hse_hbp;
 	unsigned int nlanes = output->dev->lanes;
 	int ret;
@@ -839,7 +609,15 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
 	if (ret)
 		return ret;
 
-	ret = cdns_dphy_validate(dsi, dsi_cfg, dphy_cfg, mode, mode_valid_check);
+	phy_mipi_dphy_get_default_config(mode->crtc_clock * 1000,
+					 mipi_dsi_pixel_format_to_bpp(output->dev->format),
+					 nlanes, phy_cfg);
+
+	ret = cdns_dsi_adjust_phy_config(dsi, dsi_cfg, phy_cfg, mode, mode_valid_check);
+	if (ret)
+		return ret;
+
+	ret = phy_validate(dsi->dphy, PHY_MODE_MIPI_DPHY, 0, &output->phy_opts);
 	if (ret)
 		return ret;
 
@@ -852,7 +630,7 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
 	 * is empty before we start a receiving a new line on the DPI
 	 * interface.
 	 */
-	if ((u64)dphy_cfg->lane_bps * mode_to_dpi_hfp(mode) * nlanes <
+	if ((u64)phy_cfg->hs_clk_rate * mode_to_dpi_hfp(mode) * nlanes <
 	    (u64)dsi_hss_hsa_hse_hbp *
 	    (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000)
 		return -EINVAL;
@@ -882,7 +660,6 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
 	struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
 	struct cdns_dsi *dsi = input_to_dsi(input);
 	struct cdns_dsi_output *output = &dsi->output;
-	struct cdns_dphy_cfg dphy_cfg;
 	struct cdns_dsi_cfg dsi_cfg;
 	int bpp, ret;
 
@@ -902,7 +679,7 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
 	if ((mode->hdisplay * bpp) % 32)
 		return MODE_H_ILLEGAL;
 
-	ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg, true);
+	ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, true);
 	if (ret)
 		return MODE_BAD;
 
@@ -925,9 +702,9 @@ static void cdns_dsi_bridge_disable(struct drm_bridge *bridge)
 	pm_runtime_put(dsi->base.dev);
 }
 
-static void cdns_dsi_hs_init(struct cdns_dsi *dsi,
-			     const struct cdns_dphy_cfg *dphy_cfg)
+static void cdns_dsi_hs_init(struct cdns_dsi *dsi)
 {
+	struct cdns_dsi_output *output = &dsi->output;
 	u32 status;
 
 	/*
@@ -938,30 +715,10 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi,
 	       DPHY_CMN_PDN | DPHY_PLL_PDN,
 	       dsi->regs + MCTL_DPHY_CFG0);
 
-	/*
-	 * Configure the internal PSM clk divider so that the DPHY has a
-	 * 1MHz clk (or something close).
-	 */
-	WARN_ON_ONCE(cdns_dphy_setup_psm(dsi->dphy));
-
-	/*
-	 * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
-	 * and 8 data lanes, each clk lane can be attache different set of
-	 * data lanes. The 2 groups are named 'left' and 'right', so here we
-	 * just say that we want the 'left' clk lane to drive the 'left' data
-	 * lanes.
-	 */
-	cdns_dphy_set_clk_lane_cfg(dsi->dphy, DPHY_CLK_CFG_LEFT_DRIVES_LEFT);
-
-	/*
-	 * Configure the DPHY PLL that will be used to generate the TX byte
-	 * clk.
-	 */
-	cdns_dphy_set_pll_cfg(dsi->dphy, dphy_cfg);
-
-	/* Start TX state machine. */
-	writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN,
-	       dsi->dphy->regs + DPHY_CMN_SSM);
+	phy_init(dsi->dphy);
+	phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY);
+	phy_configure(dsi->dphy, &output->phy_opts);
+	phy_power_on(dsi->dphy);
 
 	/* Activate the PLL and wait until it's locked. */
 	writel(PLL_LOCKED, dsi->regs + MCTL_MAIN_STS_CLR);
@@ -971,7 +728,7 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi,
 					status & PLL_LOCKED, 100, 100));
 	/* De-assert data and clock reset lines. */
 	writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN |
-	       DPHY_D_RSTB(dphy_cfg->nlanes) | DPHY_C_RSTB,
+	       DPHY_D_RSTB(output->dev->lanes) | DPHY_C_RSTB,
 	       dsi->regs + MCTL_DPHY_CFG0);
 }
 
@@ -1017,7 +774,7 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
 	struct cdns_dsi *dsi = input_to_dsi(input);
 	struct cdns_dsi_output *output = &dsi->output;
 	struct drm_display_mode *mode;
-	struct cdns_dphy_cfg dphy_cfg;
+	struct phy_configure_opts_mipi_dphy *phy_cfg = &output->phy_opts.mipi_dphy;
 	unsigned long tx_byte_period;
 	struct cdns_dsi_cfg dsi_cfg;
 	u32 tmp, reg_wakeup, div;
@@ -1030,9 +787,9 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
 	bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format);
 	nlanes = output->dev->lanes;
 
-	WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg, false));
+	WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, false));
 
-	cdns_dsi_hs_init(dsi, &dphy_cfg);
+	cdns_dsi_hs_init(dsi);
 	cdns_dsi_init_link(dsi);
 
 	writel(HBP_LEN(dsi_cfg.hbp) | HSA_LEN(dsi_cfg.hsa),
@@ -1068,9 +825,8 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
 		tmp -= DIV_ROUND_UP(DSI_EOT_PKT_SIZE, nlanes);
 
 	tx_byte_period = DIV_ROUND_DOWN_ULL((u64)NSEC_PER_SEC * 8,
-					    dphy_cfg.lane_bps);
-	reg_wakeup = cdns_dphy_get_wakeup_time_ns(dsi->dphy) /
-		     tx_byte_period;
+					    phy_cfg->hs_clk_rate);
+	reg_wakeup = (phy_cfg->hs_prepare + phy_cfg->hs_zero) / tx_byte_period;
 	writel(REG_WAKEUP_TIME(reg_wakeup) | REG_LINE_DURATION(tmp),
 	       dsi->regs + VID_DPHY_TIME);
 
@@ -1384,8 +1140,6 @@ static int __maybe_unused cdns_dsi_resume(struct device *dev)
 	reset_control_deassert(dsi->dsi_p_rst);
 	clk_prepare_enable(dsi->dsi_p_clk);
 	clk_prepare_enable(dsi->dsi_sys_clk);
-	clk_prepare_enable(dsi->dphy->psm_clk);
-	clk_prepare_enable(dsi->dphy->pll_ref_clk);
 
 	return 0;
 }
@@ -1394,8 +1148,6 @@ static int __maybe_unused cdns_dsi_suspend(struct device *dev)
 {
 	struct cdns_dsi *dsi = dev_get_drvdata(dev);
 
-	clk_disable_unprepare(dsi->dphy->pll_ref_clk);
-	clk_disable_unprepare(dsi->dphy->psm_clk);
 	clk_disable_unprepare(dsi->dsi_sys_clk);
 	clk_disable_unprepare(dsi->dsi_p_clk);
 	reset_control_assert(dsi->dsi_p_rst);
@@ -1406,121 +1158,6 @@ static int __maybe_unused cdns_dsi_suspend(struct device *dev)
 static UNIVERSAL_DEV_PM_OPS(cdns_dsi_pm_ops, cdns_dsi_suspend, cdns_dsi_resume,
 			    NULL);
 
-static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
-{
-	/* Default wakeup time is 800 ns (in a simulated environment). */
-	return 800;
-}
-
-static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
-				      const struct cdns_dphy_cfg *cfg)
-{
-	u32 fbdiv_low, fbdiv_high;
-
-	fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
-	fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
-
-	writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
-	       DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
-	       DPHY_CMN_OPDIV(cfg->pll_opdiv),
-	       dphy->regs + DPHY_CMN_OPIPDIV);
-	writel(DPHY_CMN_FBDIV_FROM_REG |
-	       DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
-	       dphy->regs + DPHY_CMN_FBDIV);
-	writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
-	       DPHY_CMN_PWM_DIV(0x8),
-	       dphy->regs + DPHY_CMN_PWM);
-}
-
-static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
-{
-	writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
-	       dphy->regs + DPHY_PSM_CFG);
-}
-
-/*
- * This is the reference implementation of DPHY hooks. Specific integration of
- * this IP may have to re-implement some of them depending on how they decided
- * to wire things in the SoC.
- */
-static const struct cdns_dphy_ops ref_dphy_ops = {
-	.get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
-	.set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
-	.set_psm_div = cdns_dphy_ref_set_psm_div,
-};
-
-static const struct of_device_id cdns_dphy_of_match[] = {
-	{ .compatible = "cdns,dphy", .data = &ref_dphy_ops },
-	{ /* sentinel */ },
-};
-
-static struct cdns_dphy *cdns_dphy_probe(struct platform_device *pdev)
-{
-	const struct of_device_id *match;
-	struct cdns_dphy *dphy;
-	struct of_phandle_args args;
-	struct resource res;
-	int ret;
-
-	ret = of_parse_phandle_with_args(pdev->dev.of_node, "phys",
-					 "#phy-cells", 0, &args);
-	if (ret)
-		return ERR_PTR(-ENOENT);
-
-	match = of_match_node(cdns_dphy_of_match, args.np);
-	if (!match || !match->data)
-		return ERR_PTR(-EINVAL);
-
-	dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
-	if (!dphy)
-		return ERR_PTR(-ENOMEM);
-
-	dphy->ops = match->data;
-
-	ret = of_address_to_resource(args.np, 0, &res);
-	if (ret)
-		return ERR_PTR(ret);
-
-	dphy->regs = devm_ioremap_resource(&pdev->dev, &res);
-	if (IS_ERR(dphy->regs))
-		return ERR_CAST(dphy->regs);
-
-	dphy->psm_clk = of_clk_get_by_name(args.np, "psm");
-	if (IS_ERR(dphy->psm_clk))
-		return ERR_CAST(dphy->psm_clk);
-
-	dphy->pll_ref_clk = of_clk_get_by_name(args.np, "pll_ref");
-	if (IS_ERR(dphy->pll_ref_clk)) {
-		ret = PTR_ERR(dphy->pll_ref_clk);
-		goto err_put_psm_clk;
-	}
-
-	if (dphy->ops->probe) {
-		ret = dphy->ops->probe(dphy);
-		if (ret)
-			goto err_put_pll_ref_clk;
-	}
-
-	return dphy;
-
-err_put_pll_ref_clk:
-	clk_put(dphy->pll_ref_clk);
-
-err_put_psm_clk:
-	clk_put(dphy->psm_clk);
-
-	return ERR_PTR(ret);
-}
-
-static void cdns_dphy_remove(struct cdns_dphy *dphy)
-{
-	if (dphy->ops->remove)
-		dphy->ops->remove(dphy);
-
-	clk_put(dphy->pll_ref_clk);
-	clk_put(dphy->psm_clk);
-}
-
 static int cdns_dsi_drm_probe(struct platform_device *pdev)
 {
 	struct cdns_dsi *dsi;
@@ -1559,13 +1196,13 @@ static int cdns_dsi_drm_probe(struct platform_device *pdev)
 	if (irq < 0)
 		return irq;
 
-	dsi->dphy = cdns_dphy_probe(pdev);
+	dsi->dphy = devm_phy_get(&pdev->dev, "dphy");
 	if (IS_ERR(dsi->dphy))
 		return PTR_ERR(dsi->dphy);
 
 	ret = clk_prepare_enable(dsi->dsi_p_clk);
 	if (ret)
-		goto err_remove_dphy;
+		return ret;
 
 	val = readl(dsi->regs + ID_REG);
 	if (REV_VENDOR_ID(val) != 0xcad) {
@@ -1623,9 +1260,6 @@ static int cdns_dsi_drm_probe(struct platform_device *pdev)
 err_disable_pclk:
 	clk_disable_unprepare(dsi->dsi_p_clk);
 
-err_remove_dphy:
-	cdns_dphy_remove(dsi->dphy);
-
 	return ret;
 }
 
@@ -1635,7 +1269,6 @@ static int cdns_dsi_drm_remove(struct platform_device *pdev)
 
 	mipi_dsi_host_unregister(&dsi->base);
 	pm_runtime_disable(&pdev->dev);
-	cdns_dphy_remove(dsi->dphy);
 
 	return 0;
 }
diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c
index 1d0abba03f37..cde12b3aa4d4 100644
--- a/drivers/phy/cadence/cdns-dphy.c
+++ b/drivers/phy/cadence/cdns-dphy.c
@@ -227,7 +227,7 @@ static int cdns_dphy_config_from_opts(struct phy *phy,
 	if (ret)
 		return ret;
 
-	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) * 1000;
+	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) / 1000;
 
 	return 0;
 }
-- 
git-series 0.9.1

^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices
  2019-01-09  9:33 [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (8 preceding siblings ...)
  2019-01-09  9:33 ` [PATCH v4 9/9] drm/bridge: cdns: Convert to phy framework Maxime Ripard
@ 2019-01-09 12:28 ` Sakari Ailus
  9 siblings, 0 replies; 21+ messages in thread
From: Sakari Ailus @ 2019-01-09 12:28 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Thomas Petazzoni, Laurent Pinchart,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

On Wed, Jan 09, 2019 at 10:33:17AM +0100, Maxime Ripard wrote:
> Hi,
> 
> Here is a set of patches to allow the phy framework consumers to test and
> apply runtime configurations.
> 
> This is needed to support more phy classes that require tuning based on
> parameters depending on the current use case of the device, in addition to
> the power state management already provided by the current functions.
> 
> A first test bed for that API are the MIPI D-PHY devices. There's a number
> of solutions that have been used so far to support these phy, most of the
> time being an ad-hoc driver in the consumer.
> 
> That approach has a big shortcoming though, which is that this is quite
> difficult to deal with consumers integrated with multiple variants of phy,
> of multiple consumers integrated with the same phy.
> 
> The latter case can be found in the Cadence DSI bridge, and the CSI
> transceiver and receivers. All of them are integrated with the same phy, or
> can be integrated with different phy, depending on the implementation.
> 
> I've looked at all the MIPI DSI drivers I could find, and gathered all the
> parameters I could find. The interface should be complete, and most of the
> drivers can be converted in the future. The current set converts two of
> them: the above mentionned Cadence DSI driver so that the v4l2 drivers can
> use them, and the Allwinner MIPI-DSI driver.
> 
> Let me know what you think,
> Maxime
> 
> Changes from v3
>   - Rebased on 5.0-rc1
>   - Added the fixes suggested by Sakari

Thanks!

For patches 1--3:

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 7/9] dt-bindings: phy: Move the Cadence D-PHY bindings
  2019-01-09  9:33 ` [PATCH v4 7/9] dt-bindings: phy: Move the Cadence D-PHY bindings Maxime Ripard
@ 2019-01-09 12:39   ` Sakari Ailus
  0 siblings, 0 replies; 21+ messages in thread
From: Sakari Ailus @ 2019-01-09 12:39 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Thomas Petazzoni, Laurent Pinchart,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

Hi Maxime,

On Wed, Jan 09, 2019 at 10:33:24AM +0100, Maxime Ripard wrote:
> The Cadence D-PHY bindings was defined as part of the DSI block so far.
> However, since it's now going to be a separate driver, we need to move the
> binding to a file of its own.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>

Reviewed-by: Sakari Ailus <sakari.ailus@linux.intel.com>

Could you also send this to the devicetree list, please?

> ---
>  Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt | 21 +-------
>  Documentation/devicetree/bindings/phy/cdns,dphy.txt           | 20 +++++++-
>  2 files changed, 20 insertions(+), 21 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/phy/cdns,dphy.txt
> 
> diff --git a/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt b/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
> index f5725bb6c61c..525a4bfd8634 100644
> --- a/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
> +++ b/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
> @@ -31,28 +31,7 @@ Required subnodes:
>  - one subnode per DSI device connected on the DSI bus. Each DSI device should
>    contain a reg property encoding its virtual channel.
>  
> -Cadence DPHY
> -============
> -
> -Cadence DPHY block.
> -
> -Required properties:
> -- compatible: should be set to "cdns,dphy".
> -- reg: physical base address and length of the DPHY registers.
> -- clocks: DPHY reference clocks.
> -- clock-names: must contain "psm" and "pll_ref".
> -- #phy-cells: must be set to 0.
> -
> -
>  Example:
> -	dphy0: dphy@fd0e0000{
> -		compatible = "cdns,dphy";
> -		reg = <0x0 0xfd0e0000 0x0 0x1000>;
> -		clocks = <&psm_clk>, <&pll_ref_clk>;
> -		clock-names = "psm", "pll_ref";
> -		#phy-cells = <0>;
> -	};
> -
>  	dsi0: dsi@fd0c0000 {
>  		compatible = "cdns,dsi";
>  		reg = <0x0 0xfd0c0000 0x0 0x1000>;
> diff --git a/Documentation/devicetree/bindings/phy/cdns,dphy.txt b/Documentation/devicetree/bindings/phy/cdns,dphy.txt
> new file mode 100644
> index 000000000000..1095bc4e72d9
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/phy/cdns,dphy.txt
> @@ -0,0 +1,20 @@
> +Cadence DPHY
> +============
> +
> +Cadence DPHY block.
> +
> +Required properties:
> +- compatible: should be set to "cdns,dphy".
> +- reg: physical base address and length of the DPHY registers.
> +- clocks: DPHY reference clocks.
> +- clock-names: must contain "psm" and "pll_ref".
> +- #phy-cells: must be set to 0.
> +
> +Example:
> +	dphy0: dphy@fd0e0000{
> +		compatible = "cdns,dphy";
> +		reg = <0x0 0xfd0e0000 0x0 0x1000>;
> +		clocks = <&psm_clk>, <&pll_ref_clk>;
> +		clock-names = "psm", "pll_ref";
> +		#phy-cells = <0>;
> +	};
> -- 
> git-series 0.9.1

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 8/9] phy: Add Cadence D-PHY support
  2019-01-09  9:33 ` [PATCH v4 8/9] phy: Add Cadence D-PHY support Maxime Ripard
@ 2019-01-09 12:41   ` Sakari Ailus
  2019-01-17 13:53   ` Sean Paul
  1 sibling, 0 replies; 21+ messages in thread
From: Sakari Ailus @ 2019-01-09 12:41 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Thomas Petazzoni, Laurent Pinchart,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

On Wed, Jan 09, 2019 at 10:33:25AM +0100, Maxime Ripard wrote:
> Cadence has designed a D-PHY that can be used by the, currently in tree,
> DSI bridge (DRM), CSI Transceiver and CSI Receiver (v4l2) drivers.
> 
> Only the DSI driver has an ad-hoc driver for that phy at the moment, while
> the v4l2 drivers are completely missing any phy support. In order to make
> that phy support available to all these drivers, without having to
> duplicate that code three times, let's create a generic phy framework
> driver.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>

Reviewed-by: Sakari Ailus <sakari.ailus@linux.intel.com>

-- 
Sakari Ailus

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration
  2019-01-09  9:33 ` [PATCH v4 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
@ 2019-01-16  8:36   ` Kishon Vijay Abraham I
  2019-01-17 13:33   ` Sean Paul
  1 sibling, 0 replies; 21+ messages in thread
From: Kishon Vijay Abraham I @ 2019-01-16  8:36 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

Hi Maxime,

On 09/01/19 3:03 PM, Maxime Ripard wrote:
> The current configuration of the DSI bridge and its associated D-PHY is
> intertwined. In order to ease the future conversion to the phy framework
> for the D-PHY part, let's split the configuration in two.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>

Can we get Ack from DRM MAINTAINERS?

Thanks
Kishon
> ---
>  drivers/gpu/drm/bridge/cdns-dsi.c | 96 ++++++++++++++++++++++----------
>  1 file changed, 68 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
> index ce9496d13986..3ac6dd524b6d 100644
> --- a/drivers/gpu/drm/bridge/cdns-dsi.c
> +++ b/drivers/gpu/drm/bridge/cdns-dsi.c
> @@ -545,6 +545,11 @@ bridge_to_cdns_dsi_input(struct drm_bridge *bridge)
>  	return container_of(bridge, struct cdns_dsi_input, bridge);
>  }
>  
> +static unsigned int mode_to_dpi_hfp(const struct drm_display_mode *mode)
> +{
> +	return mode->crtc_hsync_start - mode->crtc_hdisplay;
> +}
> +
>  static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
>  				     struct cdns_dphy_cfg *cfg,
>  				     unsigned int dpi_htotal,
> @@ -731,14 +736,12 @@ static unsigned int dpi_to_dsi_timing(unsigned int dpi_timing,
>  static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
>  			     const struct drm_display_mode *mode,
>  			     struct cdns_dsi_cfg *dsi_cfg,
> -			     struct cdns_dphy_cfg *dphy_cfg,
>  			     bool mode_valid_check)
>  {
> -	unsigned long dsi_htotal = 0, dsi_hss_hsa_hse_hbp = 0;
>  	struct cdns_dsi_output *output = &dsi->output;
> -	unsigned int dsi_hfp_ext = 0, dpi_hfp, tmp;
> +	unsigned int tmp;
>  	bool sync_pulse = false;
> -	int bpp, nlanes, ret;
> +	int bpp, nlanes;
>  
>  	memset(dsi_cfg, 0, sizeof(*dsi_cfg));
>  
> @@ -757,8 +760,6 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
>  		       mode->crtc_hsync_end : mode->crtc_hsync_start);
>  
>  	dsi_cfg->hbp = dpi_to_dsi_timing(tmp, bpp, DSI_HBP_FRAME_OVERHEAD);
> -	dsi_htotal += dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
> -	dsi_hss_hsa_hse_hbp += dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
>  
>  	if (sync_pulse) {
>  		if (mode_valid_check)
> @@ -768,49 +769,90 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
>  
>  		dsi_cfg->hsa = dpi_to_dsi_timing(tmp, bpp,
>  						 DSI_HSA_FRAME_OVERHEAD);
> -		dsi_htotal += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
> -		dsi_hss_hsa_hse_hbp += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
>  	}
>  
>  	dsi_cfg->hact = dpi_to_dsi_timing(mode_valid_check ?
>  					  mode->hdisplay : mode->crtc_hdisplay,
>  					  bpp, 0);
> -	dsi_htotal += dsi_cfg->hact;
> +	dsi_cfg->hfp = dpi_to_dsi_timing(mode_to_dpi_hfp(mode), bpp,
> +					 DSI_HFP_FRAME_OVERHEAD);
>  
> -	if (mode_valid_check)
> -		dpi_hfp = mode->hsync_start - mode->hdisplay;
> -	else
> -		dpi_hfp = mode->crtc_hsync_start - mode->crtc_hdisplay;
> +	return 0;
> +}
> +
> +static int cdns_dphy_validate(struct cdns_dsi *dsi,
> +			      struct cdns_dsi_cfg *dsi_cfg,
> +			      struct cdns_dphy_cfg *dphy_cfg,
> +			      const struct drm_display_mode *mode,
> +			      bool mode_valid_check)
> +{
> +	struct cdns_dsi_output *output = &dsi->output;
> +	unsigned long dsi_htotal;
> +	unsigned int dsi_hfp_ext = 0;
> +
> +	int ret;
>  
> -	dsi_cfg->hfp = dpi_to_dsi_timing(dpi_hfp, bpp, DSI_HFP_FRAME_OVERHEAD);
> +	dsi_htotal = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
> +	if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> +		dsi_htotal += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
> +
> +	dsi_htotal += dsi_cfg->hact;
>  	dsi_htotal += dsi_cfg->hfp + DSI_HFP_FRAME_OVERHEAD;
>  
>  	if (mode_valid_check)
>  		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
> -						mode->htotal, bpp,
> +						mode->htotal,
>  						mode->clock * 1000,
> -						dsi_htotal, nlanes,
> +						mipi_dsi_pixel_format_to_bpp(output->dev->format),
> +						dsi_htotal,
> +						output->dev->lanes,
>  						&dsi_hfp_ext);
>  	else
>  		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
> -						mode->crtc_htotal, bpp,
> +						mode->crtc_htotal,
> +						mipi_dsi_pixel_format_to_bpp(output->dev->format),
>  						mode->crtc_clock * 1000,
> -						dsi_htotal, nlanes,
> +						dsi_htotal,
> +						output->dev->lanes,
>  						&dsi_hfp_ext);
> -
>  	if (ret)
>  		return ret;
>  
>  	dsi_cfg->hfp += dsi_hfp_ext;
> -	dsi_htotal += dsi_hfp_ext;
> -	dsi_cfg->htotal = dsi_htotal;
> +	dsi_cfg->htotal = dsi_htotal + dsi_hfp_ext;
> +
> +	return 0;
> +}
> +
> +static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
> +			       const struct drm_display_mode *mode,
> +			       struct cdns_dsi_cfg *dsi_cfg,
> +			       struct cdns_dphy_cfg *dphy_cfg,
> +			       bool mode_valid_check)
> +{
> +	struct cdns_dsi_output *output = &dsi->output;
> +	unsigned long dsi_hss_hsa_hse_hbp;
> +	unsigned int nlanes = output->dev->lanes;
> +	int ret;
> +
> +	ret = cdns_dsi_mode2cfg(dsi, mode, dsi_cfg, mode_valid_check);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdns_dphy_validate(dsi, dsi_cfg, dphy_cfg, mode, mode_valid_check);
> +	if (ret)
> +		return ret;
> +
> +	dsi_hss_hsa_hse_hbp = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
> +	if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> +		dsi_hss_hsa_hse_hbp += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
>  
>  	/*
>  	 * Make sure DPI(HFP) > DSI(HSS+HSA+HSE+HBP) to guarantee that the FIFO
>  	 * is empty before we start a receiving a new line on the DPI
>  	 * interface.
>  	 */
> -	if ((u64)dphy_cfg->lane_bps * dpi_hfp * nlanes <
> +	if ((u64)dphy_cfg->lane_bps * mode_to_dpi_hfp(mode) * nlanes <
>  	    (u64)dsi_hss_hsa_hse_hbp *
>  	    (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000)
>  		return -EINVAL;
> @@ -842,7 +884,7 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
>  	struct cdns_dsi_output *output = &dsi->output;
>  	struct cdns_dphy_cfg dphy_cfg;
>  	struct cdns_dsi_cfg dsi_cfg;
> -	int bpp, nlanes, ret;
> +	int bpp, ret;
>  
>  	/*
>  	 * VFP_DSI should be less than VFP_DPI and VFP_DSI should be at
> @@ -860,11 +902,9 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
>  	if ((mode->hdisplay * bpp) % 32)
>  		return MODE_H_ILLEGAL;
>  
> -	nlanes = output->dev->lanes;
> -
> -	ret = cdns_dsi_mode2cfg(dsi, mode, &dsi_cfg, &dphy_cfg, true);
> +	ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg, true);
>  	if (ret)
> -		return MODE_CLOCK_RANGE;
> +		return MODE_BAD;
>  
>  	return MODE_OK;
>  }
> @@ -990,7 +1030,7 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
>  	bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format);
>  	nlanes = output->dev->lanes;
>  
> -	WARN_ON_ONCE(cdns_dsi_mode2cfg(dsi, mode, &dsi_cfg, &dphy_cfg, false));
> +	WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg, false));
>  
>  	cdns_dsi_hs_init(dsi, &dphy_cfg);
>  	cdns_dsi_init_link(dsi);
> 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 9/9] drm/bridge: cdns: Convert to phy framework
  2019-01-09  9:33 ` [PATCH v4 9/9] drm/bridge: cdns: Convert to phy framework Maxime Ripard
@ 2019-01-16  8:37   ` Kishon Vijay Abraham I
  2019-01-17 13:53   ` Sean Paul
  1 sibling, 0 replies; 21+ messages in thread
From: Kishon Vijay Abraham I @ 2019-01-16  8:37 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

Hi,

On 09/01/19 3:03 PM, Maxime Ripard wrote:
> Now that we have everything we need in the phy framework to allow to tune
> the phy parameters, let's convert the Cadence DSI bridge to that API
> instead of creating a ad-hoc driver for its phy.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>

For this too, need ACKs from DRM MAINTAINERS.

Thanks
Kishon
> ---
>  drivers/gpu/drm/bridge/Kconfig    |   1 +-
>  drivers/gpu/drm/bridge/cdns-dsi.c | 485 +++----------------------------
>  drivers/phy/cadence/cdns-dphy.c   |   2 +-
>  3 files changed, 61 insertions(+), 427 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 2fee47b0d50b..8840f396a7b6 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -30,6 +30,7 @@ config DRM_CDNS_DSI
>  	select DRM_KMS_HELPER
>  	select DRM_MIPI_DSI
>  	select DRM_PANEL_BRIDGE
> +	select GENERIC_PHY_MIPI_DPHY
>  	depends on OF
>  	help
>  	  Support Cadence DPI to DSI bridge. This is an internal
> diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
> index 3ac6dd524b6d..7b432257ffbe 100644
> --- a/drivers/gpu/drm/bridge/cdns-dsi.c
> +++ b/drivers/gpu/drm/bridge/cdns-dsi.c
> @@ -21,6 +21,9 @@
>  #include <linux/pm_runtime.h>
>  #include <linux/reset.h>
>  
> +#include <linux/phy/phy.h>
> +#include <linux/phy/phy-mipi-dphy.h>
> +
>  #define IP_CONF				0x0
>  #define SP_HS_FIFO_DEPTH(x)		(((x) & GENMASK(30, 26)) >> 26)
>  #define SP_LP_FIFO_DEPTH(x)		(((x) & GENMASK(25, 21)) >> 21)
> @@ -419,44 +422,11 @@
>  #define DSI_NULL_FRAME_OVERHEAD		6
>  #define DSI_EOT_PKT_SIZE		4
>  
> -#define REG_WAKEUP_TIME_NS		800
> -#define DPHY_PLL_RATE_HZ		108000000
> -
> -/* DPHY registers */
> -#define DPHY_PMA_CMN(reg)		(reg)
> -#define DPHY_PMA_LCLK(reg)		(0x100 + (reg))
> -#define DPHY_PMA_LDATA(lane, reg)	(0x200 + ((lane) * 0x100) + (reg))
> -#define DPHY_PMA_RCLK(reg)		(0x600 + (reg))
> -#define DPHY_PMA_RDATA(lane, reg)	(0x700 + ((lane) * 0x100) + (reg))
> -#define DPHY_PCS(reg)			(0xb00 + (reg))
> -
> -#define DPHY_CMN_SSM			DPHY_PMA_CMN(0x20)
> -#define DPHY_CMN_SSM_EN			BIT(0)
> -#define DPHY_CMN_TX_MODE_EN		BIT(9)
> -
> -#define DPHY_CMN_PWM			DPHY_PMA_CMN(0x40)
> -#define DPHY_CMN_PWM_DIV(x)		((x) << 20)
> -#define DPHY_CMN_PWM_LOW(x)		((x) << 10)
> -#define DPHY_CMN_PWM_HIGH(x)		(x)
> -
> -#define DPHY_CMN_FBDIV			DPHY_PMA_CMN(0x4c)
> -#define DPHY_CMN_FBDIV_VAL(low, high)	(((high) << 11) | ((low) << 22))
> -#define DPHY_CMN_FBDIV_FROM_REG		(BIT(10) | BIT(21))
> -
> -#define DPHY_CMN_OPIPDIV		DPHY_PMA_CMN(0x50)
> -#define DPHY_CMN_IPDIV_FROM_REG		BIT(0)
> -#define DPHY_CMN_IPDIV(x)		((x) << 1)
> -#define DPHY_CMN_OPDIV_FROM_REG		BIT(6)
> -#define DPHY_CMN_OPDIV(x)		((x) << 7)
> -
> -#define DPHY_PSM_CFG			DPHY_PCS(0x4)
> -#define DPHY_PSM_CFG_FROM_REG		BIT(0)
> -#define DPHY_PSM_CLK_DIV(x)		((x) << 1)
> -
>  struct cdns_dsi_output {
>  	struct mipi_dsi_device *dev;
>  	struct drm_panel *panel;
>  	struct drm_bridge *bridge;
> +	union phy_configure_opts phy_opts;
>  };
>  
>  enum cdns_dsi_input_id {
> @@ -465,14 +435,6 @@ enum cdns_dsi_input_id {
>  	CDNS_DSC_INPUT,
>  };
>  
> -struct cdns_dphy_cfg {
> -	u8 pll_ipdiv;
> -	u8 pll_opdiv;
> -	u16 pll_fbdiv;
> -	unsigned long lane_bps;
> -	unsigned int nlanes;
> -};
> -
>  struct cdns_dsi_cfg {
>  	unsigned int hfp;
>  	unsigned int hsa;
> @@ -481,34 +443,6 @@ struct cdns_dsi_cfg {
>  	unsigned int htotal;
>  };
>  
> -struct cdns_dphy;
> -
> -enum cdns_dphy_clk_lane_cfg {
> -	DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
> -	DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
> -	DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
> -	DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
> -};
> -
> -struct cdns_dphy_ops {
> -	int (*probe)(struct cdns_dphy *dphy);
> -	void (*remove)(struct cdns_dphy *dphy);
> -	void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
> -	void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
> -				 enum cdns_dphy_clk_lane_cfg cfg);
> -	void (*set_pll_cfg)(struct cdns_dphy *dphy,
> -			    const struct cdns_dphy_cfg *cfg);
> -	unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
> -};
> -
> -struct cdns_dphy {
> -	struct cdns_dphy_cfg cfg;
> -	void __iomem *regs;
> -	struct clk *psm_clk;
> -	struct clk *pll_ref_clk;
> -	const struct cdns_dphy_ops *ops;
> -};
> -
>  struct cdns_dsi_input {
>  	enum cdns_dsi_input_id id;
>  	struct drm_bridge bridge;
> @@ -526,7 +460,7 @@ struct cdns_dsi {
>  	struct reset_control *dsi_p_rst;
>  	struct clk *dsi_sys_clk;
>  	bool link_initialized;
> -	struct cdns_dphy *dphy;
> +	struct phy *dphy;
>  };
>  
>  static inline struct cdns_dsi *input_to_dsi(struct cdns_dsi_input *input)
> @@ -550,175 +484,6 @@ static unsigned int mode_to_dpi_hfp(const struct drm_display_mode *mode)
>  	return mode->crtc_hsync_start - mode->crtc_hdisplay;
>  }
>  
> -static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
> -				     struct cdns_dphy_cfg *cfg,
> -				     unsigned int dpi_htotal,
> -				     unsigned int dpi_bpp,
> -				     unsigned int dpi_hz,
> -				     unsigned int dsi_htotal,
> -				     unsigned int dsi_nlanes,
> -				     unsigned int *dsi_hfp_ext)
> -{
> -	u64 dlane_bps, dlane_bps_max, fbdiv, fbdiv_max, adj_dsi_htotal;
> -	unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk);
> -
> -	memset(cfg, 0, sizeof(*cfg));
> -
> -	cfg->nlanes = dsi_nlanes;
> -
> -	if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
> -		return -EINVAL;
> -	else if (pll_ref_hz < 19200000)
> -		cfg->pll_ipdiv = 1;
> -	else if (pll_ref_hz < 38400000)
> -		cfg->pll_ipdiv = 2;
> -	else if (pll_ref_hz < 76800000)
> -		cfg->pll_ipdiv = 4;
> -	else
> -		cfg->pll_ipdiv = 8;
> -
> -	/*
> -	 * Make sure DSI htotal is aligned on a lane boundary when calculating
> -	 * the expected data rate. This is done by extending HFP in case of
> -	 * misalignment.
> -	 */
> -	adj_dsi_htotal = dsi_htotal;
> -	if (dsi_htotal % dsi_nlanes)
> -		adj_dsi_htotal += dsi_nlanes - (dsi_htotal % dsi_nlanes);
> -
> -	dlane_bps = (u64)dpi_hz * adj_dsi_htotal;
> -
> -	/* data rate in bytes/sec is not an integer, refuse the mode. */
> -	if (do_div(dlane_bps, dsi_nlanes * dpi_htotal))
> -		return -EINVAL;
> -
> -	/* data rate was in bytes/sec, convert to bits/sec. */
> -	dlane_bps *= 8;
> -
> -	if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
> -		return -EINVAL;
> -	else if (dlane_bps >= 1250000000)
> -		cfg->pll_opdiv = 1;
> -	else if (dlane_bps >= 630000000)
> -		cfg->pll_opdiv = 2;
> -	else if (dlane_bps >= 320000000)
> -		cfg->pll_opdiv = 4;
> -	else if (dlane_bps >= 160000000)
> -		cfg->pll_opdiv = 8;
> -
> -	/*
> -	 * Allow a deviation of 0.2% on the per-lane data rate to try to
> -	 * recover a potential mismatch between DPI and PPI clks.
> -	 */
> -	dlane_bps_max = dlane_bps + DIV_ROUND_DOWN_ULL(dlane_bps, 500);
> -	fbdiv_max = DIV_ROUND_DOWN_ULL(dlane_bps_max * 2 *
> -				       cfg->pll_opdiv * cfg->pll_ipdiv,
> -				       pll_ref_hz);
> -	fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv *
> -				 cfg->pll_ipdiv,
> -				 pll_ref_hz);
> -
> -	/*
> -	 * Iterate over all acceptable fbdiv and try to find an adjusted DSI
> -	 * htotal length providing an exact match.
> -	 *
> -	 * Note that we could do something even trickier by relying on the fact
> -	 * that a new line is not necessarily aligned on a lane boundary, so,
> -	 * by making adj_dsi_htotal non aligned on a dsi_lanes we can improve a
> -	 * bit the precision. With this, the step would be
> -	 *
> -	 *	pll_ref_hz / (2 * opdiv * ipdiv * nlanes)
> -	 *
> -	 * instead of
> -	 *
> -	 *	pll_ref_hz / (2 * opdiv * ipdiv)
> -	 *
> -	 * The drawback of this approach is that we would need to make sure the
> -	 * number or lines is a multiple of the realignment periodicity which is
> -	 * a function of the number of lanes and the original misalignment. For
> -	 * example, for NLANES = 4 and HTOTAL % NLANES = 3, it takes 4 lines
> -	 * to realign on a lane:
> -	 * LINE 0: expected number of bytes, starts emitting first byte of
> -	 *	   LINE 1 on LANE 3
> -	 * LINE 1: expected number of bytes, starts emitting first 2 bytes of
> -	 *	   LINE 2 on LANES 2 and 3
> -	 * LINE 2: expected number of bytes, starts emitting first 3 bytes of
> -	 *	   of LINE 3 on LANES 1, 2 and 3
> -	 * LINE 3: one byte less, now things are realigned on LANE 0 for LINE 4
> -	 *
> -	 * I figured this extra complexity was not worth the benefit, but if
> -	 * someone really has unfixable mismatch, that would be something to
> -	 * investigate.
> -	 */
> -	for (; fbdiv <= fbdiv_max; fbdiv++) {
> -		u32 rem;
> -
> -		adj_dsi_htotal = (u64)fbdiv * pll_ref_hz * dsi_nlanes *
> -				 dpi_htotal;
> -
> -		/*
> -		 * Do the division in 2 steps to avoid an overflow on the
> -		 * divider.
> -		 */
> -		rem = do_div(adj_dsi_htotal, dpi_hz);
> -		if (rem)
> -			continue;
> -
> -		rem = do_div(adj_dsi_htotal,
> -			     cfg->pll_opdiv * cfg->pll_ipdiv * 2 * 8);
> -		if (rem)
> -			continue;
> -
> -		cfg->pll_fbdiv = fbdiv;
> -		*dsi_hfp_ext = adj_dsi_htotal - dsi_htotal;
> -		break;
> -	}
> -
> -	/* No match, let's just reject the display mode. */
> -	if (!cfg->pll_fbdiv)
> -		return -EINVAL;
> -
> -	dlane_bps = DIV_ROUND_DOWN_ULL((u64)dpi_hz * adj_dsi_htotal * 8,
> -				       dsi_nlanes * dpi_htotal);
> -	cfg->lane_bps = dlane_bps;
> -
> -	return 0;
> -}
> -
> -static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
> -{
> -	unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
> -	unsigned long psm_div;
> -
> -	if (!psm_clk_hz || psm_clk_hz > 100000000)
> -		return -EINVAL;
> -
> -	psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
> -	if (dphy->ops->set_psm_div)
> -		dphy->ops->set_psm_div(dphy, psm_div);
> -
> -	return 0;
> -}
> -
> -static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
> -				       enum cdns_dphy_clk_lane_cfg cfg)
> -{
> -	if (dphy->ops->set_clk_lane_cfg)
> -		dphy->ops->set_clk_lane_cfg(dphy, cfg);
> -}
> -
> -static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
> -				  const struct cdns_dphy_cfg *cfg)
> -{
> -	if (dphy->ops->set_pll_cfg)
> -		dphy->ops->set_pll_cfg(dphy, cfg);
> -}
> -
> -static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
> -{
> -	return dphy->ops->get_wakeup_time_ns(dphy);
> -}
> -
>  static unsigned int dpi_to_dsi_timing(unsigned int dpi_timing,
>  				      unsigned int dpi_bpp,
>  				      unsigned int dsi_pkt_overhead)
> @@ -780,17 +545,20 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
>  	return 0;
>  }
>  
> -static int cdns_dphy_validate(struct cdns_dsi *dsi,
> +static int cdns_dsi_adjust_phy_config(struct cdns_dsi *dsi,
>  			      struct cdns_dsi_cfg *dsi_cfg,
> -			      struct cdns_dphy_cfg *dphy_cfg,
> +			      struct phy_configure_opts_mipi_dphy *phy_cfg,
>  			      const struct drm_display_mode *mode,
>  			      bool mode_valid_check)
>  {
>  	struct cdns_dsi_output *output = &dsi->output;
> +	unsigned long long dlane_bps;
> +	unsigned long adj_dsi_htotal;
>  	unsigned long dsi_htotal;
> -	unsigned int dsi_hfp_ext = 0;
> -
> -	int ret;
> +	unsigned long dpi_htotal;
> +	unsigned long dpi_hz;
> +	unsigned int dsi_hfp_ext;
> +	unsigned int lanes = output->dev->lanes;
>  
>  	dsi_htotal = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
>  	if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> @@ -799,25 +567,27 @@ static int cdns_dphy_validate(struct cdns_dsi *dsi,
>  	dsi_htotal += dsi_cfg->hact;
>  	dsi_htotal += dsi_cfg->hfp + DSI_HFP_FRAME_OVERHEAD;
>  
> -	if (mode_valid_check)
> -		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
> -						mode->htotal,
> -						mode->clock * 1000,
> -						mipi_dsi_pixel_format_to_bpp(output->dev->format),
> -						dsi_htotal,
> -						output->dev->lanes,
> -						&dsi_hfp_ext);
> -	else
> -		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
> -						mode->crtc_htotal,
> -						mipi_dsi_pixel_format_to_bpp(output->dev->format),
> -						mode->crtc_clock * 1000,
> -						dsi_htotal,
> -						output->dev->lanes,
> -						&dsi_hfp_ext);
> -	if (ret)
> -		return ret;
> +	/*
> +	 * Make sure DSI htotal is aligned on a lane boundary when calculating
> +	 * the expected data rate. This is done by extending HFP in case of
> +	 * misalignment.
> +	 */
> +	adj_dsi_htotal = dsi_htotal;
> +	if (dsi_htotal % lanes)
> +		adj_dsi_htotal += lanes - (dsi_htotal % lanes);
> +
> +	dpi_hz = (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000;
> +	dlane_bps = (unsigned long long)dpi_hz * adj_dsi_htotal;
> +
> +	/* data rate in bytes/sec is not an integer, refuse the mode. */
> +	dpi_htotal = mode_valid_check ? mode->htotal : mode->crtc_htotal;
> +	if (do_div(dlane_bps, lanes * dpi_htotal))
> +		return -EINVAL;
>  
> +	/* data rate was in bytes/sec, convert to bits/sec. */
> +	phy_cfg->hs_clk_rate = dlane_bps * 8;
> +
> +	dsi_hfp_ext = adj_dsi_htotal - dsi_htotal;
>  	dsi_cfg->hfp += dsi_hfp_ext;
>  	dsi_cfg->htotal = dsi_htotal + dsi_hfp_ext;
>  
> @@ -827,10 +597,10 @@ static int cdns_dphy_validate(struct cdns_dsi *dsi,
>  static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
>  			       const struct drm_display_mode *mode,
>  			       struct cdns_dsi_cfg *dsi_cfg,
> -			       struct cdns_dphy_cfg *dphy_cfg,
>  			       bool mode_valid_check)
>  {
>  	struct cdns_dsi_output *output = &dsi->output;
> +	struct phy_configure_opts_mipi_dphy *phy_cfg = &output->phy_opts.mipi_dphy;
>  	unsigned long dsi_hss_hsa_hse_hbp;
>  	unsigned int nlanes = output->dev->lanes;
>  	int ret;
> @@ -839,7 +609,15 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
>  	if (ret)
>  		return ret;
>  
> -	ret = cdns_dphy_validate(dsi, dsi_cfg, dphy_cfg, mode, mode_valid_check);
> +	phy_mipi_dphy_get_default_config(mode->crtc_clock * 1000,
> +					 mipi_dsi_pixel_format_to_bpp(output->dev->format),
> +					 nlanes, phy_cfg);
> +
> +	ret = cdns_dsi_adjust_phy_config(dsi, dsi_cfg, phy_cfg, mode, mode_valid_check);
> +	if (ret)
> +		return ret;
> +
> +	ret = phy_validate(dsi->dphy, PHY_MODE_MIPI_DPHY, 0, &output->phy_opts);
>  	if (ret)
>  		return ret;
>  
> @@ -852,7 +630,7 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
>  	 * is empty before we start a receiving a new line on the DPI
>  	 * interface.
>  	 */
> -	if ((u64)dphy_cfg->lane_bps * mode_to_dpi_hfp(mode) * nlanes <
> +	if ((u64)phy_cfg->hs_clk_rate * mode_to_dpi_hfp(mode) * nlanes <
>  	    (u64)dsi_hss_hsa_hse_hbp *
>  	    (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000)
>  		return -EINVAL;
> @@ -882,7 +660,6 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
>  	struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
>  	struct cdns_dsi *dsi = input_to_dsi(input);
>  	struct cdns_dsi_output *output = &dsi->output;
> -	struct cdns_dphy_cfg dphy_cfg;
>  	struct cdns_dsi_cfg dsi_cfg;
>  	int bpp, ret;
>  
> @@ -902,7 +679,7 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
>  	if ((mode->hdisplay * bpp) % 32)
>  		return MODE_H_ILLEGAL;
>  
> -	ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg, true);
> +	ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, true);
>  	if (ret)
>  		return MODE_BAD;
>  
> @@ -925,9 +702,9 @@ static void cdns_dsi_bridge_disable(struct drm_bridge *bridge)
>  	pm_runtime_put(dsi->base.dev);
>  }
>  
> -static void cdns_dsi_hs_init(struct cdns_dsi *dsi,
> -			     const struct cdns_dphy_cfg *dphy_cfg)
> +static void cdns_dsi_hs_init(struct cdns_dsi *dsi)
>  {
> +	struct cdns_dsi_output *output = &dsi->output;
>  	u32 status;
>  
>  	/*
> @@ -938,30 +715,10 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi,
>  	       DPHY_CMN_PDN | DPHY_PLL_PDN,
>  	       dsi->regs + MCTL_DPHY_CFG0);
>  
> -	/*
> -	 * Configure the internal PSM clk divider so that the DPHY has a
> -	 * 1MHz clk (or something close).
> -	 */
> -	WARN_ON_ONCE(cdns_dphy_setup_psm(dsi->dphy));
> -
> -	/*
> -	 * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
> -	 * and 8 data lanes, each clk lane can be attache different set of
> -	 * data lanes. The 2 groups are named 'left' and 'right', so here we
> -	 * just say that we want the 'left' clk lane to drive the 'left' data
> -	 * lanes.
> -	 */
> -	cdns_dphy_set_clk_lane_cfg(dsi->dphy, DPHY_CLK_CFG_LEFT_DRIVES_LEFT);
> -
> -	/*
> -	 * Configure the DPHY PLL that will be used to generate the TX byte
> -	 * clk.
> -	 */
> -	cdns_dphy_set_pll_cfg(dsi->dphy, dphy_cfg);
> -
> -	/* Start TX state machine. */
> -	writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN,
> -	       dsi->dphy->regs + DPHY_CMN_SSM);
> +	phy_init(dsi->dphy);
> +	phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY);
> +	phy_configure(dsi->dphy, &output->phy_opts);
> +	phy_power_on(dsi->dphy);
>  
>  	/* Activate the PLL and wait until it's locked. */
>  	writel(PLL_LOCKED, dsi->regs + MCTL_MAIN_STS_CLR);
> @@ -971,7 +728,7 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi,
>  					status & PLL_LOCKED, 100, 100));
>  	/* De-assert data and clock reset lines. */
>  	writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN |
> -	       DPHY_D_RSTB(dphy_cfg->nlanes) | DPHY_C_RSTB,
> +	       DPHY_D_RSTB(output->dev->lanes) | DPHY_C_RSTB,
>  	       dsi->regs + MCTL_DPHY_CFG0);
>  }
>  
> @@ -1017,7 +774,7 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
>  	struct cdns_dsi *dsi = input_to_dsi(input);
>  	struct cdns_dsi_output *output = &dsi->output;
>  	struct drm_display_mode *mode;
> -	struct cdns_dphy_cfg dphy_cfg;
> +	struct phy_configure_opts_mipi_dphy *phy_cfg = &output->phy_opts.mipi_dphy;
>  	unsigned long tx_byte_period;
>  	struct cdns_dsi_cfg dsi_cfg;
>  	u32 tmp, reg_wakeup, div;
> @@ -1030,9 +787,9 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
>  	bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format);
>  	nlanes = output->dev->lanes;
>  
> -	WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg, false));
> +	WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, false));
>  
> -	cdns_dsi_hs_init(dsi, &dphy_cfg);
> +	cdns_dsi_hs_init(dsi);
>  	cdns_dsi_init_link(dsi);
>  
>  	writel(HBP_LEN(dsi_cfg.hbp) | HSA_LEN(dsi_cfg.hsa),
> @@ -1068,9 +825,8 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
>  		tmp -= DIV_ROUND_UP(DSI_EOT_PKT_SIZE, nlanes);
>  
>  	tx_byte_period = DIV_ROUND_DOWN_ULL((u64)NSEC_PER_SEC * 8,
> -					    dphy_cfg.lane_bps);
> -	reg_wakeup = cdns_dphy_get_wakeup_time_ns(dsi->dphy) /
> -		     tx_byte_period;
> +					    phy_cfg->hs_clk_rate);
> +	reg_wakeup = (phy_cfg->hs_prepare + phy_cfg->hs_zero) / tx_byte_period;
>  	writel(REG_WAKEUP_TIME(reg_wakeup) | REG_LINE_DURATION(tmp),
>  	       dsi->regs + VID_DPHY_TIME);
>  
> @@ -1384,8 +1140,6 @@ static int __maybe_unused cdns_dsi_resume(struct device *dev)
>  	reset_control_deassert(dsi->dsi_p_rst);
>  	clk_prepare_enable(dsi->dsi_p_clk);
>  	clk_prepare_enable(dsi->dsi_sys_clk);
> -	clk_prepare_enable(dsi->dphy->psm_clk);
> -	clk_prepare_enable(dsi->dphy->pll_ref_clk);
>  
>  	return 0;
>  }
> @@ -1394,8 +1148,6 @@ static int __maybe_unused cdns_dsi_suspend(struct device *dev)
>  {
>  	struct cdns_dsi *dsi = dev_get_drvdata(dev);
>  
> -	clk_disable_unprepare(dsi->dphy->pll_ref_clk);
> -	clk_disable_unprepare(dsi->dphy->psm_clk);
>  	clk_disable_unprepare(dsi->dsi_sys_clk);
>  	clk_disable_unprepare(dsi->dsi_p_clk);
>  	reset_control_assert(dsi->dsi_p_rst);
> @@ -1406,121 +1158,6 @@ static int __maybe_unused cdns_dsi_suspend(struct device *dev)
>  static UNIVERSAL_DEV_PM_OPS(cdns_dsi_pm_ops, cdns_dsi_suspend, cdns_dsi_resume,
>  			    NULL);
>  
> -static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
> -{
> -	/* Default wakeup time is 800 ns (in a simulated environment). */
> -	return 800;
> -}
> -
> -static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
> -				      const struct cdns_dphy_cfg *cfg)
> -{
> -	u32 fbdiv_low, fbdiv_high;
> -
> -	fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
> -	fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
> -
> -	writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
> -	       DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
> -	       DPHY_CMN_OPDIV(cfg->pll_opdiv),
> -	       dphy->regs + DPHY_CMN_OPIPDIV);
> -	writel(DPHY_CMN_FBDIV_FROM_REG |
> -	       DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
> -	       dphy->regs + DPHY_CMN_FBDIV);
> -	writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
> -	       DPHY_CMN_PWM_DIV(0x8),
> -	       dphy->regs + DPHY_CMN_PWM);
> -}
> -
> -static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
> -{
> -	writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
> -	       dphy->regs + DPHY_PSM_CFG);
> -}
> -
> -/*
> - * This is the reference implementation of DPHY hooks. Specific integration of
> - * this IP may have to re-implement some of them depending on how they decided
> - * to wire things in the SoC.
> - */
> -static const struct cdns_dphy_ops ref_dphy_ops = {
> -	.get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
> -	.set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
> -	.set_psm_div = cdns_dphy_ref_set_psm_div,
> -};
> -
> -static const struct of_device_id cdns_dphy_of_match[] = {
> -	{ .compatible = "cdns,dphy", .data = &ref_dphy_ops },
> -	{ /* sentinel */ },
> -};
> -
> -static struct cdns_dphy *cdns_dphy_probe(struct platform_device *pdev)
> -{
> -	const struct of_device_id *match;
> -	struct cdns_dphy *dphy;
> -	struct of_phandle_args args;
> -	struct resource res;
> -	int ret;
> -
> -	ret = of_parse_phandle_with_args(pdev->dev.of_node, "phys",
> -					 "#phy-cells", 0, &args);
> -	if (ret)
> -		return ERR_PTR(-ENOENT);
> -
> -	match = of_match_node(cdns_dphy_of_match, args.np);
> -	if (!match || !match->data)
> -		return ERR_PTR(-EINVAL);
> -
> -	dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
> -	if (!dphy)
> -		return ERR_PTR(-ENOMEM);
> -
> -	dphy->ops = match->data;
> -
> -	ret = of_address_to_resource(args.np, 0, &res);
> -	if (ret)
> -		return ERR_PTR(ret);
> -
> -	dphy->regs = devm_ioremap_resource(&pdev->dev, &res);
> -	if (IS_ERR(dphy->regs))
> -		return ERR_CAST(dphy->regs);
> -
> -	dphy->psm_clk = of_clk_get_by_name(args.np, "psm");
> -	if (IS_ERR(dphy->psm_clk))
> -		return ERR_CAST(dphy->psm_clk);
> -
> -	dphy->pll_ref_clk = of_clk_get_by_name(args.np, "pll_ref");
> -	if (IS_ERR(dphy->pll_ref_clk)) {
> -		ret = PTR_ERR(dphy->pll_ref_clk);
> -		goto err_put_psm_clk;
> -	}
> -
> -	if (dphy->ops->probe) {
> -		ret = dphy->ops->probe(dphy);
> -		if (ret)
> -			goto err_put_pll_ref_clk;
> -	}
> -
> -	return dphy;
> -
> -err_put_pll_ref_clk:
> -	clk_put(dphy->pll_ref_clk);
> -
> -err_put_psm_clk:
> -	clk_put(dphy->psm_clk);
> -
> -	return ERR_PTR(ret);
> -}
> -
> -static void cdns_dphy_remove(struct cdns_dphy *dphy)
> -{
> -	if (dphy->ops->remove)
> -		dphy->ops->remove(dphy);
> -
> -	clk_put(dphy->pll_ref_clk);
> -	clk_put(dphy->psm_clk);
> -}
> -
>  static int cdns_dsi_drm_probe(struct platform_device *pdev)
>  {
>  	struct cdns_dsi *dsi;
> @@ -1559,13 +1196,13 @@ static int cdns_dsi_drm_probe(struct platform_device *pdev)
>  	if (irq < 0)
>  		return irq;
>  
> -	dsi->dphy = cdns_dphy_probe(pdev);
> +	dsi->dphy = devm_phy_get(&pdev->dev, "dphy");
>  	if (IS_ERR(dsi->dphy))
>  		return PTR_ERR(dsi->dphy);
>  
>  	ret = clk_prepare_enable(dsi->dsi_p_clk);
>  	if (ret)
> -		goto err_remove_dphy;
> +		return ret;
>  
>  	val = readl(dsi->regs + ID_REG);
>  	if (REV_VENDOR_ID(val) != 0xcad) {
> @@ -1623,9 +1260,6 @@ static int cdns_dsi_drm_probe(struct platform_device *pdev)
>  err_disable_pclk:
>  	clk_disable_unprepare(dsi->dsi_p_clk);
>  
> -err_remove_dphy:
> -	cdns_dphy_remove(dsi->dphy);
> -
>  	return ret;
>  }
>  
> @@ -1635,7 +1269,6 @@ static int cdns_dsi_drm_remove(struct platform_device *pdev)
>  
>  	mipi_dsi_host_unregister(&dsi->base);
>  	pm_runtime_disable(&pdev->dev);
> -	cdns_dphy_remove(dsi->dphy);
>  
>  	return 0;
>  }
> diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c
> index 1d0abba03f37..cde12b3aa4d4 100644
> --- a/drivers/phy/cadence/cdns-dphy.c
> +++ b/drivers/phy/cadence/cdns-dphy.c
> @@ -227,7 +227,7 @@ static int cdns_dphy_config_from_opts(struct phy *phy,
>  	if (ret)
>  		return ret;
>  
> -	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) * 1000;
> +	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) / 1000;
>  
>  	return 0;
>  }
> 

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration
  2019-01-09  9:33 ` [PATCH v4 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
  2019-01-16  8:36   ` Kishon Vijay Abraham I
@ 2019-01-17 13:33   ` Sean Paul
  2019-01-21 15:43     ` Maxime Ripard
  1 sibling, 1 reply; 21+ messages in thread
From: Sean Paul @ 2019-01-17 13:33 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Thomas Petazzoni, Laurent Pinchart,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

On Wed, Jan 09, 2019 at 10:33:23AM +0100, Maxime Ripard wrote:
> The current configuration of the DSI bridge and its associated D-PHY is
> intertwined. In order to ease the future conversion to the phy framework
> for the D-PHY part, let's split the configuration in two.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>  drivers/gpu/drm/bridge/cdns-dsi.c | 96 ++++++++++++++++++++++----------
>  1 file changed, 68 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
> index ce9496d13986..3ac6dd524b6d 100644
> --- a/drivers/gpu/drm/bridge/cdns-dsi.c
> +++ b/drivers/gpu/drm/bridge/cdns-dsi.c
> @@ -545,6 +545,11 @@ bridge_to_cdns_dsi_input(struct drm_bridge *bridge)
>  	return container_of(bridge, struct cdns_dsi_input, bridge);
>  }
>  
> +static unsigned int mode_to_dpi_hfp(const struct drm_display_mode *mode)
> +{
> +	return mode->crtc_hsync_start - mode->crtc_hdisplay;
> +}
> +
>  static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
>  				     struct cdns_dphy_cfg *cfg,
>  				     unsigned int dpi_htotal,
> @@ -731,14 +736,12 @@ static unsigned int dpi_to_dsi_timing(unsigned int dpi_timing,
>  static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
>  			     const struct drm_display_mode *mode,
>  			     struct cdns_dsi_cfg *dsi_cfg,
> -			     struct cdns_dphy_cfg *dphy_cfg,
>  			     bool mode_valid_check)
>  {
> -	unsigned long dsi_htotal = 0, dsi_hss_hsa_hse_hbp = 0;
>  	struct cdns_dsi_output *output = &dsi->output;
> -	unsigned int dsi_hfp_ext = 0, dpi_hfp, tmp;
> +	unsigned int tmp;
>  	bool sync_pulse = false;
> -	int bpp, nlanes, ret;
> +	int bpp, nlanes;
>  
>  	memset(dsi_cfg, 0, sizeof(*dsi_cfg));
>  
> @@ -757,8 +760,6 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
>  		       mode->crtc_hsync_end : mode->crtc_hsync_start);
>  
>  	dsi_cfg->hbp = dpi_to_dsi_timing(tmp, bpp, DSI_HBP_FRAME_OVERHEAD);
> -	dsi_htotal += dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
> -	dsi_hss_hsa_hse_hbp += dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
>  
>  	if (sync_pulse) {
>  		if (mode_valid_check)
> @@ -768,49 +769,90 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
>  
>  		dsi_cfg->hsa = dpi_to_dsi_timing(tmp, bpp,
>  						 DSI_HSA_FRAME_OVERHEAD);
> -		dsi_htotal += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
> -		dsi_hss_hsa_hse_hbp += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
>  	}
>  
>  	dsi_cfg->hact = dpi_to_dsi_timing(mode_valid_check ?
>  					  mode->hdisplay : mode->crtc_hdisplay,
>  					  bpp, 0);
> -	dsi_htotal += dsi_cfg->hact;
> +	dsi_cfg->hfp = dpi_to_dsi_timing(mode_to_dpi_hfp(mode), bpp,
> +					 DSI_HFP_FRAME_OVERHEAD);

We're throwing away the mode_valid_check switch here to flip between crtc_h*
value and h* value. Is that intentional? We're using it above for hdisplay, so
it's a bit confusing.

>  
> -	if (mode_valid_check)
> -		dpi_hfp = mode->hsync_start - mode->hdisplay;
> -	else
> -		dpi_hfp = mode->crtc_hsync_start - mode->crtc_hdisplay;
> +	return 0;
> +}
> +
> +static int cdns_dphy_validate(struct cdns_dsi *dsi,
> +			      struct cdns_dsi_cfg *dsi_cfg,
> +			      struct cdns_dphy_cfg *dphy_cfg,
> +			      const struct drm_display_mode *mode,
> +			      bool mode_valid_check)
> +{
> +	struct cdns_dsi_output *output = &dsi->output;
> +	unsigned long dsi_htotal;
> +	unsigned int dsi_hfp_ext = 0;
> +
> +	int ret;
>  
> -	dsi_cfg->hfp = dpi_to_dsi_timing(dpi_hfp, bpp, DSI_HFP_FRAME_OVERHEAD);
> +	dsi_htotal = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
> +	if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> +		dsi_htotal += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
> +
> +	dsi_htotal += dsi_cfg->hact;
>  	dsi_htotal += dsi_cfg->hfp + DSI_HFP_FRAME_OVERHEAD;
>  
>  	if (mode_valid_check)
>  		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
> -						mode->htotal, bpp,
> +						mode->htotal,
>  						mode->clock * 1000,
> -						dsi_htotal, nlanes,
> +						mipi_dsi_pixel_format_to_bpp(output->dev->format),
> +						dsi_htotal,
> +						output->dev->lanes,
>  						&dsi_hfp_ext);
>  	else
>  		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
> -						mode->crtc_htotal, bpp,
> +						mode->crtc_htotal,
> +						mipi_dsi_pixel_format_to_bpp(output->dev->format),
>  						mode->crtc_clock * 1000,
> -						dsi_htotal, nlanes,
> +						dsi_htotal,
> +						output->dev->lanes,
>  						&dsi_hfp_ext);
> -
>  	if (ret)
>  		return ret;
>  
>  	dsi_cfg->hfp += dsi_hfp_ext;
> -	dsi_htotal += dsi_hfp_ext;
> -	dsi_cfg->htotal = dsi_htotal;
> +	dsi_cfg->htotal = dsi_htotal + dsi_hfp_ext;
> +
> +	return 0;
> +}
> +
> +static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
> +			       const struct drm_display_mode *mode,
> +			       struct cdns_dsi_cfg *dsi_cfg,
> +			       struct cdns_dphy_cfg *dphy_cfg,
> +			       bool mode_valid_check)
> +{
> +	struct cdns_dsi_output *output = &dsi->output;
> +	unsigned long dsi_hss_hsa_hse_hbp;
> +	unsigned int nlanes = output->dev->lanes;
> +	int ret;
> +
> +	ret = cdns_dsi_mode2cfg(dsi, mode, dsi_cfg, mode_valid_check);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdns_dphy_validate(dsi, dsi_cfg, dphy_cfg, mode, mode_valid_check);
> +	if (ret)
> +		return ret;
> +
> +	dsi_hss_hsa_hse_hbp = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
> +	if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> +		dsi_hss_hsa_hse_hbp += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
>  
>  	/*
>  	 * Make sure DPI(HFP) > DSI(HSS+HSA+HSE+HBP) to guarantee that the FIFO
>  	 * is empty before we start a receiving a new line on the DPI
>  	 * interface.
>  	 */
> -	if ((u64)dphy_cfg->lane_bps * dpi_hfp * nlanes <
> +	if ((u64)dphy_cfg->lane_bps * mode_to_dpi_hfp(mode) * nlanes <
>  	    (u64)dsi_hss_hsa_hse_hbp *
>  	    (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000)
>  		return -EINVAL;
> @@ -842,7 +884,7 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
>  	struct cdns_dsi_output *output = &dsi->output;
>  	struct cdns_dphy_cfg dphy_cfg;
>  	struct cdns_dsi_cfg dsi_cfg;
> -	int bpp, nlanes, ret;
> +	int bpp, ret;
>  
>  	/*
>  	 * VFP_DSI should be less than VFP_DPI and VFP_DSI should be at
> @@ -860,11 +902,9 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
>  	if ((mode->hdisplay * bpp) % 32)
>  		return MODE_H_ILLEGAL;
>  
> -	nlanes = output->dev->lanes;
> -
> -	ret = cdns_dsi_mode2cfg(dsi, mode, &dsi_cfg, &dphy_cfg, true);
> +	ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg, true);
>  	if (ret)
> -		return MODE_CLOCK_RANGE;
> +		return MODE_BAD;
>  
>  	return MODE_OK;
>  }
> @@ -990,7 +1030,7 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
>  	bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format);
>  	nlanes = output->dev->lanes;
>  
> -	WARN_ON_ONCE(cdns_dsi_mode2cfg(dsi, mode, &dsi_cfg, &dphy_cfg, false));
> +	WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg, false));
>  
>  	cdns_dsi_hs_init(dsi, &dphy_cfg);
>  	cdns_dsi_init_link(dsi);
> -- 
> git-series 0.9.1

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 8/9] phy: Add Cadence D-PHY support
  2019-01-09  9:33 ` [PATCH v4 8/9] phy: Add Cadence D-PHY support Maxime Ripard
  2019-01-09 12:41   ` Sakari Ailus
@ 2019-01-17 13:53   ` Sean Paul
  2019-01-21 13:39     ` Maxime Ripard
  1 sibling, 1 reply; 21+ messages in thread
From: Sean Paul @ 2019-01-17 13:53 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Rafal Ciepiela, Krzysztof Witos,
	linux-kernel, dri-devel, Chen-Yu Tsai, Laurent Pinchart,
	Thomas Petazzoni, linux-arm-kernel, linux-media

On Wed, Jan 09, 2019 at 10:33:25AM +0100, Maxime Ripard wrote:
> Cadence has designed a D-PHY that can be used by the, currently in tree,
> DSI bridge (DRM), CSI Transceiver and CSI Receiver (v4l2) drivers.
> 
> Only the DSI driver has an ad-hoc driver for that phy at the moment, while
> the v4l2 drivers are completely missing any phy support. In order to make
> that phy support available to all these drivers, without having to
> duplicate that code three times, let's create a generic phy framework
> driver.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>  drivers/phy/cadence/Kconfig     |  13 +-
>  drivers/phy/cadence/Makefile    |   1 +-
>  drivers/phy/cadence/cdns-dphy.c | 389 +++++++++++++++++++++++++++++++++-
>  3 files changed, 402 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/phy/cadence/cdns-dphy.c
> 
> diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
> index 2b8c0851ff33..31f18b67dd7c 100644
> --- a/drivers/phy/cadence/Kconfig
> +++ b/drivers/phy/cadence/Kconfig
> @@ -1,6 +1,7 @@
>  #
>  # Phy drivers for Cadence PHYs
>  #
> +
>  config PHY_CADENCE_DP
>  	tristate "Cadence MHDP DisplayPort PHY driver"
>  	depends on OF
> @@ -9,9 +10,19 @@ config PHY_CADENCE_DP
>  	help
>  	  Support for Cadence MHDP DisplayPort PHY.
>  
> +config PHY_CADENCE_DPHY
> +	tristate "Cadence D-PHY Support"
> +	depends on HAS_IOMEM && OF
> +	select GENERIC_PHY
> +	select GENERIC_PHY_MIPI_DPHY
> +	help
> +	  Choose this option if you have a Cadence D-PHY in your
> +	  system. If M is selected, the module will be called
> +	  cdns-dphy.
> +
>  config PHY_CADENCE_SIERRA
>  	tristate "Cadence Sierra PHY Driver"
>  	depends on OF && HAS_IOMEM && RESET_CONTROLLER
>  	select GENERIC_PHY
>  	help
> -	  Enable this to support the Cadence Sierra PHY driver
> \ No newline at end of file
> +	  Enable this to support the Cadence Sierra PHY driver
> diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile
> index 412349af0492..2f9e3457b954 100644
> --- a/drivers/phy/cadence/Makefile
> +++ b/drivers/phy/cadence/Makefile
> @@ -1,2 +1,3 @@
>  obj-$(CONFIG_PHY_CADENCE_DP)	+= phy-cadence-dp.o
> +obj-$(CONFIG_PHY_CADENCE_DPHY)	+= cdns-dphy.o
>  obj-$(CONFIG_PHY_CADENCE_SIERRA)	+= phy-cadence-sierra.o
> diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c
> new file mode 100644
> index 000000000000..1d0abba03f37
> --- /dev/null
> +++ b/drivers/phy/cadence/cdns-dphy.c
> @@ -0,0 +1,389 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright: 2017-2018 Cadence Design Systems, Inc.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/reset.h>
> +
> +#include <linux/phy/phy.h>
> +#include <linux/phy/phy-mipi-dphy.h>
> +
> +#define REG_WAKEUP_TIME_NS		800
> +#define DPHY_PLL_RATE_HZ		108000000
> +
> +/* DPHY registers */
> +#define DPHY_PMA_CMN(reg)		(reg)
> +#define DPHY_PMA_LCLK(reg)		(0x100 + (reg))
> +#define DPHY_PMA_LDATA(lane, reg)	(0x200 + ((lane) * 0x100) + (reg))
> +#define DPHY_PMA_RCLK(reg)		(0x600 + (reg))
> +#define DPHY_PMA_RDATA(lane, reg)	(0x700 + ((lane) * 0x100) + (reg))
> +#define DPHY_PCS(reg)			(0xb00 + (reg))
> +
> +#define DPHY_CMN_SSM			DPHY_PMA_CMN(0x20)
> +#define DPHY_CMN_SSM_EN			BIT(0)
> +#define DPHY_CMN_TX_MODE_EN		BIT(9)
> +
> +#define DPHY_CMN_PWM			DPHY_PMA_CMN(0x40)
> +#define DPHY_CMN_PWM_DIV(x)		((x) << 20)
> +#define DPHY_CMN_PWM_LOW(x)		((x) << 10)
> +#define DPHY_CMN_PWM_HIGH(x)		(x)
> +
> +#define DPHY_CMN_FBDIV			DPHY_PMA_CMN(0x4c)
> +#define DPHY_CMN_FBDIV_VAL(low, high)	(((high) << 11) | ((low) << 22))
> +#define DPHY_CMN_FBDIV_FROM_REG		(BIT(10) | BIT(21))
> +
> +#define DPHY_CMN_OPIPDIV		DPHY_PMA_CMN(0x50)
> +#define DPHY_CMN_IPDIV_FROM_REG		BIT(0)
> +#define DPHY_CMN_IPDIV(x)		((x) << 1)
> +#define DPHY_CMN_OPDIV_FROM_REG		BIT(6)
> +#define DPHY_CMN_OPDIV(x)		((x) << 7)
> +
> +#define DPHY_PSM_CFG			DPHY_PCS(0x4)
> +#define DPHY_PSM_CFG_FROM_REG		BIT(0)
> +#define DPHY_PSM_CLK_DIV(x)		((x) << 1)
> +
> +#define DSI_HBP_FRAME_OVERHEAD		12
> +#define DSI_HSA_FRAME_OVERHEAD		14
> +#define DSI_HFP_FRAME_OVERHEAD		6
> +#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD	4
> +#define DSI_BLANKING_FRAME_OVERHEAD	6
> +#define DSI_NULL_FRAME_OVERHEAD		6
> +#define DSI_EOT_PKT_SIZE		4
> +
> +struct cdns_dphy_cfg {
> +	u8 pll_ipdiv;
> +	u8 pll_opdiv;
> +	u16 pll_fbdiv;
> +	unsigned int nlanes;
> +};
> +
> +enum cdns_dphy_clk_lane_cfg {
> +	DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
> +	DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
> +	DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
> +	DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
> +};
> +
> +struct cdns_dphy;
> +struct cdns_dphy_ops {
> +	int (*probe)(struct cdns_dphy *dphy);
> +	void (*remove)(struct cdns_dphy *dphy);
> +	void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
> +	void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
> +				 enum cdns_dphy_clk_lane_cfg cfg);
> +	void (*set_pll_cfg)(struct cdns_dphy *dphy,
> +			    const struct cdns_dphy_cfg *cfg);
> +	unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
> +};
> +
> +struct cdns_dphy {
> +	struct cdns_dphy_cfg cfg;
> +	void __iomem *regs;
> +	struct clk *psm_clk;
> +	struct clk *pll_ref_clk;
> +	const struct cdns_dphy_ops *ops;
> +	struct phy *phy;
> +};
> +
> +static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
> +				     struct cdns_dphy_cfg *cfg,
> +				     struct phy_configure_opts_mipi_dphy *opts,
> +				     unsigned int *dsi_hfp_ext)
> +{
> +	unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk);
> +	u64 dlane_bps;
> +
> +	memset(cfg, 0, sizeof(*cfg));
> +
> +	if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
> +		return -EINVAL;
> +	else if (pll_ref_hz < 19200000)
> +		cfg->pll_ipdiv = 1;
> +	else if (pll_ref_hz < 38400000)
> +		cfg->pll_ipdiv = 2;
> +	else if (pll_ref_hz < 76800000)
> +		cfg->pll_ipdiv = 4;
> +	else
> +		cfg->pll_ipdiv = 8;
> +
> +	dlane_bps = opts->hs_clk_rate;
> +
> +	if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
> +		return -EINVAL;
> +	else if (dlane_bps >= 1250000000)
> +		cfg->pll_opdiv = 1;
> +	else if (dlane_bps >= 630000000)
> +		cfg->pll_opdiv = 2;
> +	else if (dlane_bps >= 320000000)
> +		cfg->pll_opdiv = 4;
> +	else if (dlane_bps >= 160000000)
> +		cfg->pll_opdiv = 8;
> +
> +	cfg->pll_fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv *
> +					  cfg->pll_ipdiv,
> +					  pll_ref_hz);
> +
> +	return 0;
> +}
> +
> +static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
> +{
> +	unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
> +	unsigned long psm_div;
> +
> +	if (!psm_clk_hz || psm_clk_hz > 100000000)
> +		return -EINVAL;
> +
> +	psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
> +	if (dphy->ops->set_psm_div)
> +		dphy->ops->set_psm_div(dphy, psm_div);
> +
> +	return 0;
> +}
> +
> +static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
> +				       enum cdns_dphy_clk_lane_cfg cfg)
> +{
> +	if (dphy->ops->set_clk_lane_cfg)
> +		dphy->ops->set_clk_lane_cfg(dphy, cfg);
> +}
> +
> +static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
> +				  const struct cdns_dphy_cfg *cfg)
> +{
> +	if (dphy->ops->set_pll_cfg)
> +		dphy->ops->set_pll_cfg(dphy, cfg);
> +}
> +
> +static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
> +{
> +	return dphy->ops->get_wakeup_time_ns(dphy);
> +}
> +
> +static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
> +{
> +	/* Default wakeup time is 800 ns (in a simulated environment). */
> +	return 800;
> +}
> +
> +static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
> +				      const struct cdns_dphy_cfg *cfg)
> +{
> +	u32 fbdiv_low, fbdiv_high;
> +
> +	fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
> +	fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
> +
> +	writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
> +	       DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
> +	       DPHY_CMN_OPDIV(cfg->pll_opdiv),
> +	       dphy->regs + DPHY_CMN_OPIPDIV);
> +	writel(DPHY_CMN_FBDIV_FROM_REG |
> +	       DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
> +	       dphy->regs + DPHY_CMN_FBDIV);
> +	writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
> +	       DPHY_CMN_PWM_DIV(0x8),
> +	       dphy->regs + DPHY_CMN_PWM);
> +}
> +
> +static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
> +{
> +	writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
> +	       dphy->regs + DPHY_PSM_CFG);
> +}
> +
> +/*
> + * This is the reference implementation of DPHY hooks. Specific integration of
> + * this IP may have to re-implement some of them depending on how they decided
> + * to wire things in the SoC.
> + */
> +static const struct cdns_dphy_ops ref_dphy_ops = {
> +	.get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
> +	.set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
> +	.set_psm_div = cdns_dphy_ref_set_psm_div,
> +};
> +
> +static int cdns_dphy_config_from_opts(struct phy *phy,
> +				      struct phy_configure_opts_mipi_dphy *opts,
> +				      struct cdns_dphy_cfg *cfg)
> +{
> +	struct cdns_dphy *dphy = phy_get_drvdata(phy);
> +	unsigned int dsi_hfp_ext = 0;
> +	int ret;
> +
> +	ret = phy_mipi_dphy_config_validate(opts);
> +	if (ret)
> +		return ret;
> +
> +	ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg,
> +					opts, &dsi_hfp_ext);
> +	if (ret)
> +		return ret;
> +
> +	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) * 1000;

This should be "/ 1000" since the units of wakeup is us now (thanks to patch 2).
You've made this change in patch 9, so I think it's just a misplaced fixup.

> +
> +	return 0;
> +}
> +
> +static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode,
> +			      union phy_configure_opts *opts)
> +{
> +	struct cdns_dphy_cfg cfg = { 0 };
> +
> +	if (mode != PHY_MODE_MIPI_DPHY)
> +		return -EINVAL;
> +
> +	return cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
> +}
> +
> +static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
> +{
> +	struct cdns_dphy *dphy = phy_get_drvdata(phy);
> +	struct cdns_dphy_cfg cfg = { 0 };
> +	int ret;
> +
> +	ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Configure the internal PSM clk divider so that the DPHY has a
> +	 * 1MHz clk (or something close).
> +	 */
> +	ret = cdns_dphy_setup_psm(dphy);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
> +	 * and 8 data lanes, each clk lane can be attache different set of
> +	 * data lanes. The 2 groups are named 'left' and 'right', so here we
> +	 * just say that we want the 'left' clk lane to drive the 'left' data
> +	 * lanes.
> +	 */
> +	cdns_dphy_set_clk_lane_cfg(dphy, DPHY_CLK_CFG_LEFT_DRIVES_LEFT);
> +
> +	/*
> +	 * Configure the DPHY PLL that will be used to generate the TX byte
> +	 * clk.
> +	 */
> +	cdns_dphy_set_pll_cfg(dphy, &cfg);
> +
> +	return 0;
> +}
> +
> +static int cdns_dphy_power_on(struct phy *phy)
> +{
> +	struct cdns_dphy *dphy = phy_get_drvdata(phy);
> +
> +	clk_prepare_enable(dphy->psm_clk);
> +	clk_prepare_enable(dphy->pll_ref_clk);
> +
> +	/* Start TX state machine. */
> +	writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN, dphy->regs + DPHY_CMN_SSM);
> +
> +	return 0;
> +}
> +
> +static int cdns_dphy_power_off(struct phy *phy)
> +{
> +	struct cdns_dphy *dphy = phy_get_drvdata(phy);
> +
> +	clk_disable_unprepare(dphy->pll_ref_clk);
> +	clk_disable_unprepare(dphy->psm_clk);
> +
> +	return 0;
> +}
> +
> +static const struct phy_ops cdns_dphy_ops = {
> +	.configure	= cdns_dphy_configure,
> +	.validate	= cdns_dphy_validate,
> +	.power_on	= cdns_dphy_power_on,
> +	.power_off	= cdns_dphy_power_off,
> +};
> +
> +static int cdns_dphy_probe(struct platform_device *pdev)
> +{
> +	struct phy_provider *phy_provider;
> +	struct cdns_dphy *dphy;
> +	struct resource *res;
> +	int ret;
> +
> +	dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
> +	if (!dphy)
> +		return -ENOMEM;
> +	dev_set_drvdata(&pdev->dev, dphy);
> +
> +	dphy->ops = of_device_get_match_data(&pdev->dev);
> +	if (!dphy->ops)
> +		return -EINVAL;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	dphy->regs = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(dphy->regs))
> +		return PTR_ERR(dphy->regs);
> +
> +	dphy->psm_clk = devm_clk_get(&pdev->dev, "psm");
> +	if (IS_ERR(dphy->psm_clk))
> +		return PTR_ERR(dphy->psm_clk);
> +
> +	dphy->pll_ref_clk = devm_clk_get(&pdev->dev, "pll_ref");
> +	if (IS_ERR(dphy->pll_ref_clk))
> +		return PTR_ERR(dphy->pll_ref_clk);
> +
> +	if (dphy->ops->probe) {
> +		ret = dphy->ops->probe(dphy);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	dphy->phy = devm_phy_create(&pdev->dev, NULL, &cdns_dphy_ops);
> +	if (IS_ERR(dphy->phy)) {
> +		dev_err(&pdev->dev, "failed to create PHY\n");
> +		if (dphy->ops->remove)
> +			dphy->ops->remove(dphy);
> +		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 int cdns_dphy_remove(struct platform_device *pdev)
> +{
> +	struct cdns_dphy *dphy = dev_get_drvdata(&pdev->dev);
> +
> +	if (dphy->ops->remove)
> +		dphy->ops->remove(dphy);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id cdns_dphy_of_match[] = {
> +	{ .compatible = "cdns,dphy", .data = &ref_dphy_ops },
> +	{ /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, cdns_dphy_of_match);
> +
> +static struct platform_driver cdns_dphy_platform_driver = {
> +	.probe		= cdns_dphy_probe,
> +	.remove		= cdns_dphy_remove,
> +	.driver		= {
> +		.name		= "cdns-mipi-dphy",
> +		.of_match_table	= cdns_dphy_of_match,
> +	},
> +};
> +module_platform_driver(cdns_dphy_platform_driver);
> +
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin.com>");
> +MODULE_DESCRIPTION("Cadence MIPI D-PHY Driver");
> +MODULE_LICENSE("GPL");
> -- 
> git-series 0.9.1
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 9/9] drm/bridge: cdns: Convert to phy framework
  2019-01-09  9:33 ` [PATCH v4 9/9] drm/bridge: cdns: Convert to phy framework Maxime Ripard
  2019-01-16  8:37   ` Kishon Vijay Abraham I
@ 2019-01-17 13:53   ` Sean Paul
  1 sibling, 0 replies; 21+ messages in thread
From: Sean Paul @ 2019-01-17 13:53 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Rafal Ciepiela, Krzysztof Witos,
	linux-kernel, dri-devel, Chen-Yu Tsai, Laurent Pinchart,
	Thomas Petazzoni, linux-arm-kernel, linux-media

On Wed, Jan 09, 2019 at 10:33:26AM +0100, Maxime Ripard wrote:
> Now that we have everything we need in the phy framework to allow to tune
> the phy parameters, let's convert the Cadence DSI bridge to that API
> instead of creating a ad-hoc driver for its phy.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>

Aside from the wakeup change mentioned in patch 8,

Acked-by: Sean Paul <sean@poorly.run>


> ---
>  drivers/gpu/drm/bridge/Kconfig    |   1 +-
>  drivers/gpu/drm/bridge/cdns-dsi.c | 485 +++----------------------------
>  drivers/phy/cadence/cdns-dphy.c   |   2 +-
>  3 files changed, 61 insertions(+), 427 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 2fee47b0d50b..8840f396a7b6 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -30,6 +30,7 @@ config DRM_CDNS_DSI
>  	select DRM_KMS_HELPER
>  	select DRM_MIPI_DSI
>  	select DRM_PANEL_BRIDGE
> +	select GENERIC_PHY_MIPI_DPHY
>  	depends on OF
>  	help
>  	  Support Cadence DPI to DSI bridge. This is an internal
> diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
> index 3ac6dd524b6d..7b432257ffbe 100644
> --- a/drivers/gpu/drm/bridge/cdns-dsi.c
> +++ b/drivers/gpu/drm/bridge/cdns-dsi.c
> @@ -21,6 +21,9 @@
>  #include <linux/pm_runtime.h>
>  #include <linux/reset.h>
>  
> +#include <linux/phy/phy.h>
> +#include <linux/phy/phy-mipi-dphy.h>
> +
>  #define IP_CONF				0x0
>  #define SP_HS_FIFO_DEPTH(x)		(((x) & GENMASK(30, 26)) >> 26)
>  #define SP_LP_FIFO_DEPTH(x)		(((x) & GENMASK(25, 21)) >> 21)
> @@ -419,44 +422,11 @@
>  #define DSI_NULL_FRAME_OVERHEAD		6
>  #define DSI_EOT_PKT_SIZE		4
>  
> -#define REG_WAKEUP_TIME_NS		800
> -#define DPHY_PLL_RATE_HZ		108000000
> -
> -/* DPHY registers */
> -#define DPHY_PMA_CMN(reg)		(reg)
> -#define DPHY_PMA_LCLK(reg)		(0x100 + (reg))
> -#define DPHY_PMA_LDATA(lane, reg)	(0x200 + ((lane) * 0x100) + (reg))
> -#define DPHY_PMA_RCLK(reg)		(0x600 + (reg))
> -#define DPHY_PMA_RDATA(lane, reg)	(0x700 + ((lane) * 0x100) + (reg))
> -#define DPHY_PCS(reg)			(0xb00 + (reg))
> -
> -#define DPHY_CMN_SSM			DPHY_PMA_CMN(0x20)
> -#define DPHY_CMN_SSM_EN			BIT(0)
> -#define DPHY_CMN_TX_MODE_EN		BIT(9)
> -
> -#define DPHY_CMN_PWM			DPHY_PMA_CMN(0x40)
> -#define DPHY_CMN_PWM_DIV(x)		((x) << 20)
> -#define DPHY_CMN_PWM_LOW(x)		((x) << 10)
> -#define DPHY_CMN_PWM_HIGH(x)		(x)
> -
> -#define DPHY_CMN_FBDIV			DPHY_PMA_CMN(0x4c)
> -#define DPHY_CMN_FBDIV_VAL(low, high)	(((high) << 11) | ((low) << 22))
> -#define DPHY_CMN_FBDIV_FROM_REG		(BIT(10) | BIT(21))
> -
> -#define DPHY_CMN_OPIPDIV		DPHY_PMA_CMN(0x50)
> -#define DPHY_CMN_IPDIV_FROM_REG		BIT(0)
> -#define DPHY_CMN_IPDIV(x)		((x) << 1)
> -#define DPHY_CMN_OPDIV_FROM_REG		BIT(6)
> -#define DPHY_CMN_OPDIV(x)		((x) << 7)
> -
> -#define DPHY_PSM_CFG			DPHY_PCS(0x4)
> -#define DPHY_PSM_CFG_FROM_REG		BIT(0)
> -#define DPHY_PSM_CLK_DIV(x)		((x) << 1)
> -
>  struct cdns_dsi_output {
>  	struct mipi_dsi_device *dev;
>  	struct drm_panel *panel;
>  	struct drm_bridge *bridge;
> +	union phy_configure_opts phy_opts;
>  };
>  
>  enum cdns_dsi_input_id {
> @@ -465,14 +435,6 @@ enum cdns_dsi_input_id {
>  	CDNS_DSC_INPUT,
>  };
>  
> -struct cdns_dphy_cfg {
> -	u8 pll_ipdiv;
> -	u8 pll_opdiv;
> -	u16 pll_fbdiv;
> -	unsigned long lane_bps;
> -	unsigned int nlanes;
> -};
> -
>  struct cdns_dsi_cfg {
>  	unsigned int hfp;
>  	unsigned int hsa;
> @@ -481,34 +443,6 @@ struct cdns_dsi_cfg {
>  	unsigned int htotal;
>  };
>  
> -struct cdns_dphy;
> -
> -enum cdns_dphy_clk_lane_cfg {
> -	DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
> -	DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
> -	DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
> -	DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
> -};
> -
> -struct cdns_dphy_ops {
> -	int (*probe)(struct cdns_dphy *dphy);
> -	void (*remove)(struct cdns_dphy *dphy);
> -	void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
> -	void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
> -				 enum cdns_dphy_clk_lane_cfg cfg);
> -	void (*set_pll_cfg)(struct cdns_dphy *dphy,
> -			    const struct cdns_dphy_cfg *cfg);
> -	unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
> -};
> -
> -struct cdns_dphy {
> -	struct cdns_dphy_cfg cfg;
> -	void __iomem *regs;
> -	struct clk *psm_clk;
> -	struct clk *pll_ref_clk;
> -	const struct cdns_dphy_ops *ops;
> -};
> -
>  struct cdns_dsi_input {
>  	enum cdns_dsi_input_id id;
>  	struct drm_bridge bridge;
> @@ -526,7 +460,7 @@ struct cdns_dsi {
>  	struct reset_control *dsi_p_rst;
>  	struct clk *dsi_sys_clk;
>  	bool link_initialized;
> -	struct cdns_dphy *dphy;
> +	struct phy *dphy;
>  };
>  
>  static inline struct cdns_dsi *input_to_dsi(struct cdns_dsi_input *input)
> @@ -550,175 +484,6 @@ static unsigned int mode_to_dpi_hfp(const struct drm_display_mode *mode)
>  	return mode->crtc_hsync_start - mode->crtc_hdisplay;
>  }
>  
> -static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
> -				     struct cdns_dphy_cfg *cfg,
> -				     unsigned int dpi_htotal,
> -				     unsigned int dpi_bpp,
> -				     unsigned int dpi_hz,
> -				     unsigned int dsi_htotal,
> -				     unsigned int dsi_nlanes,
> -				     unsigned int *dsi_hfp_ext)
> -{
> -	u64 dlane_bps, dlane_bps_max, fbdiv, fbdiv_max, adj_dsi_htotal;
> -	unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk);
> -
> -	memset(cfg, 0, sizeof(*cfg));
> -
> -	cfg->nlanes = dsi_nlanes;
> -
> -	if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
> -		return -EINVAL;
> -	else if (pll_ref_hz < 19200000)
> -		cfg->pll_ipdiv = 1;
> -	else if (pll_ref_hz < 38400000)
> -		cfg->pll_ipdiv = 2;
> -	else if (pll_ref_hz < 76800000)
> -		cfg->pll_ipdiv = 4;
> -	else
> -		cfg->pll_ipdiv = 8;
> -
> -	/*
> -	 * Make sure DSI htotal is aligned on a lane boundary when calculating
> -	 * the expected data rate. This is done by extending HFP in case of
> -	 * misalignment.
> -	 */
> -	adj_dsi_htotal = dsi_htotal;
> -	if (dsi_htotal % dsi_nlanes)
> -		adj_dsi_htotal += dsi_nlanes - (dsi_htotal % dsi_nlanes);
> -
> -	dlane_bps = (u64)dpi_hz * adj_dsi_htotal;
> -
> -	/* data rate in bytes/sec is not an integer, refuse the mode. */
> -	if (do_div(dlane_bps, dsi_nlanes * dpi_htotal))
> -		return -EINVAL;
> -
> -	/* data rate was in bytes/sec, convert to bits/sec. */
> -	dlane_bps *= 8;
> -
> -	if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
> -		return -EINVAL;
> -	else if (dlane_bps >= 1250000000)
> -		cfg->pll_opdiv = 1;
> -	else if (dlane_bps >= 630000000)
> -		cfg->pll_opdiv = 2;
> -	else if (dlane_bps >= 320000000)
> -		cfg->pll_opdiv = 4;
> -	else if (dlane_bps >= 160000000)
> -		cfg->pll_opdiv = 8;
> -
> -	/*
> -	 * Allow a deviation of 0.2% on the per-lane data rate to try to
> -	 * recover a potential mismatch between DPI and PPI clks.
> -	 */
> -	dlane_bps_max = dlane_bps + DIV_ROUND_DOWN_ULL(dlane_bps, 500);
> -	fbdiv_max = DIV_ROUND_DOWN_ULL(dlane_bps_max * 2 *
> -				       cfg->pll_opdiv * cfg->pll_ipdiv,
> -				       pll_ref_hz);
> -	fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv *
> -				 cfg->pll_ipdiv,
> -				 pll_ref_hz);
> -
> -	/*
> -	 * Iterate over all acceptable fbdiv and try to find an adjusted DSI
> -	 * htotal length providing an exact match.
> -	 *
> -	 * Note that we could do something even trickier by relying on the fact
> -	 * that a new line is not necessarily aligned on a lane boundary, so,
> -	 * by making adj_dsi_htotal non aligned on a dsi_lanes we can improve a
> -	 * bit the precision. With this, the step would be
> -	 *
> -	 *	pll_ref_hz / (2 * opdiv * ipdiv * nlanes)
> -	 *
> -	 * instead of
> -	 *
> -	 *	pll_ref_hz / (2 * opdiv * ipdiv)
> -	 *
> -	 * The drawback of this approach is that we would need to make sure the
> -	 * number or lines is a multiple of the realignment periodicity which is
> -	 * a function of the number of lanes and the original misalignment. For
> -	 * example, for NLANES = 4 and HTOTAL % NLANES = 3, it takes 4 lines
> -	 * to realign on a lane:
> -	 * LINE 0: expected number of bytes, starts emitting first byte of
> -	 *	   LINE 1 on LANE 3
> -	 * LINE 1: expected number of bytes, starts emitting first 2 bytes of
> -	 *	   LINE 2 on LANES 2 and 3
> -	 * LINE 2: expected number of bytes, starts emitting first 3 bytes of
> -	 *	   of LINE 3 on LANES 1, 2 and 3
> -	 * LINE 3: one byte less, now things are realigned on LANE 0 for LINE 4
> -	 *
> -	 * I figured this extra complexity was not worth the benefit, but if
> -	 * someone really has unfixable mismatch, that would be something to
> -	 * investigate.
> -	 */
> -	for (; fbdiv <= fbdiv_max; fbdiv++) {
> -		u32 rem;
> -
> -		adj_dsi_htotal = (u64)fbdiv * pll_ref_hz * dsi_nlanes *
> -				 dpi_htotal;
> -
> -		/*
> -		 * Do the division in 2 steps to avoid an overflow on the
> -		 * divider.
> -		 */
> -		rem = do_div(adj_dsi_htotal, dpi_hz);
> -		if (rem)
> -			continue;
> -
> -		rem = do_div(adj_dsi_htotal,
> -			     cfg->pll_opdiv * cfg->pll_ipdiv * 2 * 8);
> -		if (rem)
> -			continue;
> -
> -		cfg->pll_fbdiv = fbdiv;
> -		*dsi_hfp_ext = adj_dsi_htotal - dsi_htotal;
> -		break;
> -	}
> -
> -	/* No match, let's just reject the display mode. */
> -	if (!cfg->pll_fbdiv)
> -		return -EINVAL;
> -
> -	dlane_bps = DIV_ROUND_DOWN_ULL((u64)dpi_hz * adj_dsi_htotal * 8,
> -				       dsi_nlanes * dpi_htotal);
> -	cfg->lane_bps = dlane_bps;
> -
> -	return 0;
> -}
> -
> -static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
> -{
> -	unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
> -	unsigned long psm_div;
> -
> -	if (!psm_clk_hz || psm_clk_hz > 100000000)
> -		return -EINVAL;
> -
> -	psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
> -	if (dphy->ops->set_psm_div)
> -		dphy->ops->set_psm_div(dphy, psm_div);
> -
> -	return 0;
> -}
> -
> -static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
> -				       enum cdns_dphy_clk_lane_cfg cfg)
> -{
> -	if (dphy->ops->set_clk_lane_cfg)
> -		dphy->ops->set_clk_lane_cfg(dphy, cfg);
> -}
> -
> -static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
> -				  const struct cdns_dphy_cfg *cfg)
> -{
> -	if (dphy->ops->set_pll_cfg)
> -		dphy->ops->set_pll_cfg(dphy, cfg);
> -}
> -
> -static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
> -{
> -	return dphy->ops->get_wakeup_time_ns(dphy);
> -}
> -
>  static unsigned int dpi_to_dsi_timing(unsigned int dpi_timing,
>  				      unsigned int dpi_bpp,
>  				      unsigned int dsi_pkt_overhead)
> @@ -780,17 +545,20 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
>  	return 0;
>  }
>  
> -static int cdns_dphy_validate(struct cdns_dsi *dsi,
> +static int cdns_dsi_adjust_phy_config(struct cdns_dsi *dsi,
>  			      struct cdns_dsi_cfg *dsi_cfg,
> -			      struct cdns_dphy_cfg *dphy_cfg,
> +			      struct phy_configure_opts_mipi_dphy *phy_cfg,
>  			      const struct drm_display_mode *mode,
>  			      bool mode_valid_check)
>  {
>  	struct cdns_dsi_output *output = &dsi->output;
> +	unsigned long long dlane_bps;
> +	unsigned long adj_dsi_htotal;
>  	unsigned long dsi_htotal;
> -	unsigned int dsi_hfp_ext = 0;
> -
> -	int ret;
> +	unsigned long dpi_htotal;
> +	unsigned long dpi_hz;
> +	unsigned int dsi_hfp_ext;
> +	unsigned int lanes = output->dev->lanes;
>  
>  	dsi_htotal = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
>  	if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> @@ -799,25 +567,27 @@ static int cdns_dphy_validate(struct cdns_dsi *dsi,
>  	dsi_htotal += dsi_cfg->hact;
>  	dsi_htotal += dsi_cfg->hfp + DSI_HFP_FRAME_OVERHEAD;
>  
> -	if (mode_valid_check)
> -		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
> -						mode->htotal,
> -						mode->clock * 1000,
> -						mipi_dsi_pixel_format_to_bpp(output->dev->format),
> -						dsi_htotal,
> -						output->dev->lanes,
> -						&dsi_hfp_ext);
> -	else
> -		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
> -						mode->crtc_htotal,
> -						mipi_dsi_pixel_format_to_bpp(output->dev->format),
> -						mode->crtc_clock * 1000,
> -						dsi_htotal,
> -						output->dev->lanes,
> -						&dsi_hfp_ext);
> -	if (ret)
> -		return ret;
> +	/*
> +	 * Make sure DSI htotal is aligned on a lane boundary when calculating
> +	 * the expected data rate. This is done by extending HFP in case of
> +	 * misalignment.
> +	 */
> +	adj_dsi_htotal = dsi_htotal;
> +	if (dsi_htotal % lanes)
> +		adj_dsi_htotal += lanes - (dsi_htotal % lanes);
> +
> +	dpi_hz = (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000;
> +	dlane_bps = (unsigned long long)dpi_hz * adj_dsi_htotal;
> +
> +	/* data rate in bytes/sec is not an integer, refuse the mode. */
> +	dpi_htotal = mode_valid_check ? mode->htotal : mode->crtc_htotal;
> +	if (do_div(dlane_bps, lanes * dpi_htotal))
> +		return -EINVAL;
>  
> +	/* data rate was in bytes/sec, convert to bits/sec. */
> +	phy_cfg->hs_clk_rate = dlane_bps * 8;
> +
> +	dsi_hfp_ext = adj_dsi_htotal - dsi_htotal;
>  	dsi_cfg->hfp += dsi_hfp_ext;
>  	dsi_cfg->htotal = dsi_htotal + dsi_hfp_ext;
>  
> @@ -827,10 +597,10 @@ static int cdns_dphy_validate(struct cdns_dsi *dsi,
>  static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
>  			       const struct drm_display_mode *mode,
>  			       struct cdns_dsi_cfg *dsi_cfg,
> -			       struct cdns_dphy_cfg *dphy_cfg,
>  			       bool mode_valid_check)
>  {
>  	struct cdns_dsi_output *output = &dsi->output;
> +	struct phy_configure_opts_mipi_dphy *phy_cfg = &output->phy_opts.mipi_dphy;
>  	unsigned long dsi_hss_hsa_hse_hbp;
>  	unsigned int nlanes = output->dev->lanes;
>  	int ret;
> @@ -839,7 +609,15 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
>  	if (ret)
>  		return ret;
>  
> -	ret = cdns_dphy_validate(dsi, dsi_cfg, dphy_cfg, mode, mode_valid_check);
> +	phy_mipi_dphy_get_default_config(mode->crtc_clock * 1000,
> +					 mipi_dsi_pixel_format_to_bpp(output->dev->format),
> +					 nlanes, phy_cfg);
> +
> +	ret = cdns_dsi_adjust_phy_config(dsi, dsi_cfg, phy_cfg, mode, mode_valid_check);
> +	if (ret)
> +		return ret;
> +
> +	ret = phy_validate(dsi->dphy, PHY_MODE_MIPI_DPHY, 0, &output->phy_opts);
>  	if (ret)
>  		return ret;
>  
> @@ -852,7 +630,7 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
>  	 * is empty before we start a receiving a new line on the DPI
>  	 * interface.
>  	 */
> -	if ((u64)dphy_cfg->lane_bps * mode_to_dpi_hfp(mode) * nlanes <
> +	if ((u64)phy_cfg->hs_clk_rate * mode_to_dpi_hfp(mode) * nlanes <
>  	    (u64)dsi_hss_hsa_hse_hbp *
>  	    (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000)
>  		return -EINVAL;
> @@ -882,7 +660,6 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
>  	struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge);
>  	struct cdns_dsi *dsi = input_to_dsi(input);
>  	struct cdns_dsi_output *output = &dsi->output;
> -	struct cdns_dphy_cfg dphy_cfg;
>  	struct cdns_dsi_cfg dsi_cfg;
>  	int bpp, ret;
>  
> @@ -902,7 +679,7 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
>  	if ((mode->hdisplay * bpp) % 32)
>  		return MODE_H_ILLEGAL;
>  
> -	ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg, true);
> +	ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, true);
>  	if (ret)
>  		return MODE_BAD;
>  
> @@ -925,9 +702,9 @@ static void cdns_dsi_bridge_disable(struct drm_bridge *bridge)
>  	pm_runtime_put(dsi->base.dev);
>  }
>  
> -static void cdns_dsi_hs_init(struct cdns_dsi *dsi,
> -			     const struct cdns_dphy_cfg *dphy_cfg)
> +static void cdns_dsi_hs_init(struct cdns_dsi *dsi)
>  {
> +	struct cdns_dsi_output *output = &dsi->output;
>  	u32 status;
>  
>  	/*
> @@ -938,30 +715,10 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi,
>  	       DPHY_CMN_PDN | DPHY_PLL_PDN,
>  	       dsi->regs + MCTL_DPHY_CFG0);
>  
> -	/*
> -	 * Configure the internal PSM clk divider so that the DPHY has a
> -	 * 1MHz clk (or something close).
> -	 */
> -	WARN_ON_ONCE(cdns_dphy_setup_psm(dsi->dphy));
> -
> -	/*
> -	 * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes
> -	 * and 8 data lanes, each clk lane can be attache different set of
> -	 * data lanes. The 2 groups are named 'left' and 'right', so here we
> -	 * just say that we want the 'left' clk lane to drive the 'left' data
> -	 * lanes.
> -	 */
> -	cdns_dphy_set_clk_lane_cfg(dsi->dphy, DPHY_CLK_CFG_LEFT_DRIVES_LEFT);
> -
> -	/*
> -	 * Configure the DPHY PLL that will be used to generate the TX byte
> -	 * clk.
> -	 */
> -	cdns_dphy_set_pll_cfg(dsi->dphy, dphy_cfg);
> -
> -	/* Start TX state machine. */
> -	writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN,
> -	       dsi->dphy->regs + DPHY_CMN_SSM);
> +	phy_init(dsi->dphy);
> +	phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY);
> +	phy_configure(dsi->dphy, &output->phy_opts);
> +	phy_power_on(dsi->dphy);
>  
>  	/* Activate the PLL and wait until it's locked. */
>  	writel(PLL_LOCKED, dsi->regs + MCTL_MAIN_STS_CLR);
> @@ -971,7 +728,7 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi,
>  					status & PLL_LOCKED, 100, 100));
>  	/* De-assert data and clock reset lines. */
>  	writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN |
> -	       DPHY_D_RSTB(dphy_cfg->nlanes) | DPHY_C_RSTB,
> +	       DPHY_D_RSTB(output->dev->lanes) | DPHY_C_RSTB,
>  	       dsi->regs + MCTL_DPHY_CFG0);
>  }
>  
> @@ -1017,7 +774,7 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
>  	struct cdns_dsi *dsi = input_to_dsi(input);
>  	struct cdns_dsi_output *output = &dsi->output;
>  	struct drm_display_mode *mode;
> -	struct cdns_dphy_cfg dphy_cfg;
> +	struct phy_configure_opts_mipi_dphy *phy_cfg = &output->phy_opts.mipi_dphy;
>  	unsigned long tx_byte_period;
>  	struct cdns_dsi_cfg dsi_cfg;
>  	u32 tmp, reg_wakeup, div;
> @@ -1030,9 +787,9 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
>  	bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format);
>  	nlanes = output->dev->lanes;
>  
> -	WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg, false));
> +	WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, false));
>  
> -	cdns_dsi_hs_init(dsi, &dphy_cfg);
> +	cdns_dsi_hs_init(dsi);
>  	cdns_dsi_init_link(dsi);
>  
>  	writel(HBP_LEN(dsi_cfg.hbp) | HSA_LEN(dsi_cfg.hsa),
> @@ -1068,9 +825,8 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge)
>  		tmp -= DIV_ROUND_UP(DSI_EOT_PKT_SIZE, nlanes);
>  
>  	tx_byte_period = DIV_ROUND_DOWN_ULL((u64)NSEC_PER_SEC * 8,
> -					    dphy_cfg.lane_bps);
> -	reg_wakeup = cdns_dphy_get_wakeup_time_ns(dsi->dphy) /
> -		     tx_byte_period;
> +					    phy_cfg->hs_clk_rate);
> +	reg_wakeup = (phy_cfg->hs_prepare + phy_cfg->hs_zero) / tx_byte_period;
>  	writel(REG_WAKEUP_TIME(reg_wakeup) | REG_LINE_DURATION(tmp),
>  	       dsi->regs + VID_DPHY_TIME);
>  
> @@ -1384,8 +1140,6 @@ static int __maybe_unused cdns_dsi_resume(struct device *dev)
>  	reset_control_deassert(dsi->dsi_p_rst);
>  	clk_prepare_enable(dsi->dsi_p_clk);
>  	clk_prepare_enable(dsi->dsi_sys_clk);
> -	clk_prepare_enable(dsi->dphy->psm_clk);
> -	clk_prepare_enable(dsi->dphy->pll_ref_clk);
>  
>  	return 0;
>  }
> @@ -1394,8 +1148,6 @@ static int __maybe_unused cdns_dsi_suspend(struct device *dev)
>  {
>  	struct cdns_dsi *dsi = dev_get_drvdata(dev);
>  
> -	clk_disable_unprepare(dsi->dphy->pll_ref_clk);
> -	clk_disable_unprepare(dsi->dphy->psm_clk);
>  	clk_disable_unprepare(dsi->dsi_sys_clk);
>  	clk_disable_unprepare(dsi->dsi_p_clk);
>  	reset_control_assert(dsi->dsi_p_rst);
> @@ -1406,121 +1158,6 @@ static int __maybe_unused cdns_dsi_suspend(struct device *dev)
>  static UNIVERSAL_DEV_PM_OPS(cdns_dsi_pm_ops, cdns_dsi_suspend, cdns_dsi_resume,
>  			    NULL);
>  
> -static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
> -{
> -	/* Default wakeup time is 800 ns (in a simulated environment). */
> -	return 800;
> -}
> -
> -static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
> -				      const struct cdns_dphy_cfg *cfg)
> -{
> -	u32 fbdiv_low, fbdiv_high;
> -
> -	fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
> -	fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
> -
> -	writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
> -	       DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
> -	       DPHY_CMN_OPDIV(cfg->pll_opdiv),
> -	       dphy->regs + DPHY_CMN_OPIPDIV);
> -	writel(DPHY_CMN_FBDIV_FROM_REG |
> -	       DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
> -	       dphy->regs + DPHY_CMN_FBDIV);
> -	writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
> -	       DPHY_CMN_PWM_DIV(0x8),
> -	       dphy->regs + DPHY_CMN_PWM);
> -}
> -
> -static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
> -{
> -	writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
> -	       dphy->regs + DPHY_PSM_CFG);
> -}
> -
> -/*
> - * This is the reference implementation of DPHY hooks. Specific integration of
> - * this IP may have to re-implement some of them depending on how they decided
> - * to wire things in the SoC.
> - */
> -static const struct cdns_dphy_ops ref_dphy_ops = {
> -	.get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
> -	.set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
> -	.set_psm_div = cdns_dphy_ref_set_psm_div,
> -};
> -
> -static const struct of_device_id cdns_dphy_of_match[] = {
> -	{ .compatible = "cdns,dphy", .data = &ref_dphy_ops },
> -	{ /* sentinel */ },
> -};
> -
> -static struct cdns_dphy *cdns_dphy_probe(struct platform_device *pdev)
> -{
> -	const struct of_device_id *match;
> -	struct cdns_dphy *dphy;
> -	struct of_phandle_args args;
> -	struct resource res;
> -	int ret;
> -
> -	ret = of_parse_phandle_with_args(pdev->dev.of_node, "phys",
> -					 "#phy-cells", 0, &args);
> -	if (ret)
> -		return ERR_PTR(-ENOENT);
> -
> -	match = of_match_node(cdns_dphy_of_match, args.np);
> -	if (!match || !match->data)
> -		return ERR_PTR(-EINVAL);
> -
> -	dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
> -	if (!dphy)
> -		return ERR_PTR(-ENOMEM);
> -
> -	dphy->ops = match->data;
> -
> -	ret = of_address_to_resource(args.np, 0, &res);
> -	if (ret)
> -		return ERR_PTR(ret);
> -
> -	dphy->regs = devm_ioremap_resource(&pdev->dev, &res);
> -	if (IS_ERR(dphy->regs))
> -		return ERR_CAST(dphy->regs);
> -
> -	dphy->psm_clk = of_clk_get_by_name(args.np, "psm");
> -	if (IS_ERR(dphy->psm_clk))
> -		return ERR_CAST(dphy->psm_clk);
> -
> -	dphy->pll_ref_clk = of_clk_get_by_name(args.np, "pll_ref");
> -	if (IS_ERR(dphy->pll_ref_clk)) {
> -		ret = PTR_ERR(dphy->pll_ref_clk);
> -		goto err_put_psm_clk;
> -	}
> -
> -	if (dphy->ops->probe) {
> -		ret = dphy->ops->probe(dphy);
> -		if (ret)
> -			goto err_put_pll_ref_clk;
> -	}
> -
> -	return dphy;
> -
> -err_put_pll_ref_clk:
> -	clk_put(dphy->pll_ref_clk);
> -
> -err_put_psm_clk:
> -	clk_put(dphy->psm_clk);
> -
> -	return ERR_PTR(ret);
> -}
> -
> -static void cdns_dphy_remove(struct cdns_dphy *dphy)
> -{
> -	if (dphy->ops->remove)
> -		dphy->ops->remove(dphy);
> -
> -	clk_put(dphy->pll_ref_clk);
> -	clk_put(dphy->psm_clk);
> -}
> -
>  static int cdns_dsi_drm_probe(struct platform_device *pdev)
>  {
>  	struct cdns_dsi *dsi;
> @@ -1559,13 +1196,13 @@ static int cdns_dsi_drm_probe(struct platform_device *pdev)
>  	if (irq < 0)
>  		return irq;
>  
> -	dsi->dphy = cdns_dphy_probe(pdev);
> +	dsi->dphy = devm_phy_get(&pdev->dev, "dphy");
>  	if (IS_ERR(dsi->dphy))
>  		return PTR_ERR(dsi->dphy);
>  
>  	ret = clk_prepare_enable(dsi->dsi_p_clk);
>  	if (ret)
> -		goto err_remove_dphy;
> +		return ret;
>  
>  	val = readl(dsi->regs + ID_REG);
>  	if (REV_VENDOR_ID(val) != 0xcad) {
> @@ -1623,9 +1260,6 @@ static int cdns_dsi_drm_probe(struct platform_device *pdev)
>  err_disable_pclk:
>  	clk_disable_unprepare(dsi->dsi_p_clk);
>  
> -err_remove_dphy:
> -	cdns_dphy_remove(dsi->dphy);
> -
>  	return ret;
>  }
>  
> @@ -1635,7 +1269,6 @@ static int cdns_dsi_drm_remove(struct platform_device *pdev)
>  
>  	mipi_dsi_host_unregister(&dsi->base);
>  	pm_runtime_disable(&pdev->dev);
> -	cdns_dphy_remove(dsi->dphy);
>  
>  	return 0;
>  }
> diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c
> index 1d0abba03f37..cde12b3aa4d4 100644
> --- a/drivers/phy/cadence/cdns-dphy.c
> +++ b/drivers/phy/cadence/cdns-dphy.c
> @@ -227,7 +227,7 @@ static int cdns_dphy_config_from_opts(struct phy *phy,
>  	if (ret)
>  		return ret;
>  
> -	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) * 1000;
> +	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) / 1000;
>  
>  	return 0;
>  }
> -- 
> git-series 0.9.1
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Sean Paul, Software Engineer, Google / Chromium OS

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 8/9] phy: Add Cadence D-PHY support
  2019-01-17 13:53   ` Sean Paul
@ 2019-01-21 13:39     ` Maxime Ripard
  0 siblings, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-21 13:39 UTC (permalink / raw)
  To: Sean Paul
  Cc: Kishon Vijay Abraham I, Rafal Ciepiela, Krzysztof Witos,
	linux-kernel, dri-devel, Chen-Yu Tsai, Laurent Pinchart,
	Thomas Petazzoni, linux-arm-kernel, linux-media

[-- Attachment #1: Type: text/plain, Size: 555 bytes --]

Hi Sean,

On Thu, Jan 17, 2019 at 08:53:22AM -0500, Sean Paul wrote:
> On Wed, Jan 09, 2019 at 10:33:25AM +0100, Maxime Ripard wrote:
> > +	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) * 1000;
> 
> This should be "/ 1000" since the units of wakeup is us now (thanks to patch 2).
> You've made this change in patch 9, so I think it's just a misplaced fixup.

Good catch, this was fixed in patch 9, but it definitely belongs there.

Thanks!
Maxime

-- 
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration
  2019-01-17 13:33   ` Sean Paul
@ 2019-01-21 15:43     ` Maxime Ripard
  0 siblings, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-21 15:43 UTC (permalink / raw)
  To: Sean Paul
  Cc: Kishon Vijay Abraham I, Thomas Petazzoni, Laurent Pinchart,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

[-- Attachment #1: Type: text/plain, Size: 1038 bytes --]

On Thu, Jan 17, 2019 at 08:33:38AM -0500, Sean Paul wrote:
> > @@ -768,49 +769,90 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
> >  
> >  		dsi_cfg->hsa = dpi_to_dsi_timing(tmp, bpp,
> >  						 DSI_HSA_FRAME_OVERHEAD);
> > -		dsi_htotal += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
> > -		dsi_hss_hsa_hse_hbp += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
> >  	}
> >  
> >  	dsi_cfg->hact = dpi_to_dsi_timing(mode_valid_check ?
> >  					  mode->hdisplay : mode->crtc_hdisplay,
> >  					  bpp, 0);
> > -	dsi_htotal += dsi_cfg->hact;
> > +	dsi_cfg->hfp = dpi_to_dsi_timing(mode_to_dpi_hfp(mode), bpp,
> > +					 DSI_HFP_FRAME_OVERHEAD);
> 
> We're throwing away the mode_valid_check switch here to flip between crtc_h*
> value and h* value. Is that intentional? We're using it above for hdisplay, so
> it's a bit confusing.

ah, right, thanks for spotting this.

I'll resend a version with those changes, thanks!
Maxime

-- 
Maxime Ripard, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH v4 4/9] sun6i: dsi: Convert to generic phy handling
  2019-01-09  9:33 ` [PATCH v4 4/9] sun6i: dsi: Convert to generic phy handling Maxime Ripard
@ 2019-02-07  8:16   ` Paul Kocialkowski
  0 siblings, 0 replies; 21+ messages in thread
From: Paul Kocialkowski @ 2019-02-07  8:16 UTC (permalink / raw)
  To: Maxime Ripard, Kishon Vijay Abraham I
  Cc: Rafal Ciepiela, Krzysztof Witos, linux-kernel, dri-devel,
	Chen-Yu Tsai, Laurent Pinchart, Thomas Petazzoni,
	linux-arm-kernel, linux-media

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_ */
-- 
Paul Kocialkowski, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com


^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2019-02-07  8:16 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-09  9:33 [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
2019-01-09  9:33 ` [PATCH v4 1/9] phy: dphy: Remove unused header Maxime Ripard
2019-01-09  9:33 ` [PATCH v4 2/9] phy: dphy: Change units of wakeup and init parameters Maxime Ripard
2019-01-09  9:33 ` [PATCH v4 3/9] phy: dphy: Clarify lanes parameter documentation Maxime Ripard
2019-01-09  9:33 ` [PATCH v4 4/9] sun6i: dsi: Convert to generic phy handling Maxime Ripard
2019-02-07  8:16   ` Paul Kocialkowski
2019-01-09  9:33 ` [PATCH v4 5/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/ Maxime Ripard
2019-01-09  9:33 ` [PATCH v4 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
2019-01-16  8:36   ` Kishon Vijay Abraham I
2019-01-17 13:33   ` Sean Paul
2019-01-21 15:43     ` Maxime Ripard
2019-01-09  9:33 ` [PATCH v4 7/9] dt-bindings: phy: Move the Cadence D-PHY bindings Maxime Ripard
2019-01-09 12:39   ` Sakari Ailus
2019-01-09  9:33 ` [PATCH v4 8/9] phy: Add Cadence D-PHY support Maxime Ripard
2019-01-09 12:41   ` Sakari Ailus
2019-01-17 13:53   ` Sean Paul
2019-01-21 13:39     ` Maxime Ripard
2019-01-09  9:33 ` [PATCH v4 9/9] drm/bridge: cdns: Convert to phy framework Maxime Ripard
2019-01-16  8:37   ` Kishon Vijay Abraham I
2019-01-17 13:53   ` Sean Paul
2019-01-09 12:28 ` [PATCH v4 0/9] phy: Add configuration interface for MIPI D-PHY devices Sakari Ailus

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).