linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices
@ 2019-01-21 15:45 Maxime Ripard
  2019-01-21 15:45 ` [PATCH v5 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-21 15:45 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,
	Maarten Lankhorst, Sean Paul, 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 v4:
  - Removed regression on the variable calculation
  - Fixed the wakeup unit
  - Collected Sean Acked-by on the last patch
  - Collected Sakari Reviewed-by on the first patch

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                             | 538 +------
 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, 894 insertions(+), 789 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 v5 1/9] phy: dphy: Remove unused header
  2019-01-21 15:45 [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
@ 2019-01-21 15:45 ` Maxime Ripard
  2019-01-21 15:45 ` [PATCH v5 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-21 15:45 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,
	Maarten Lankhorst, Sean Paul, Maxime Ripard, Sakari Ailus,
	Sakari Ailus

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

Suggested-by: Sakari Ailus <sakari.ailus@iki.fi>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
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 v5 2/9] phy: dphy: Change units of wakeup and init parameters
  2019-01-21 15:45 [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
  2019-01-21 15:45 ` [PATCH v5 1/9] phy: dphy: Remove unused header Maxime Ripard
@ 2019-01-21 15:45 ` Maxime Ripard
  2019-01-21 15:45 ` [PATCH v5 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-21 15:45 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,
	Maarten Lankhorst, Sean Paul, Maxime Ripard, Sakari Ailus,
	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>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
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 v5 3/9] phy: dphy: Clarify lanes parameter documentation
  2019-01-21 15:45 [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
  2019-01-21 15:45 ` [PATCH v5 1/9] phy: dphy: Remove unused header Maxime Ripard
  2019-01-21 15:45 ` [PATCH v5 2/9] phy: dphy: Change units of wakeup and init parameters Maxime Ripard
@ 2019-01-21 15:45 ` Maxime Ripard
  2019-01-21 15:45 ` [PATCH v5 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-21 15:45 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,
	Maarten Lankhorst, Sean Paul, Maxime Ripard, Sakari Ailus,
	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>
Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
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 v5 4/9] sun6i: dsi: Convert to generic phy handling
  2019-01-21 15:45 [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (2 preceding siblings ...)
  2019-01-21 15:45 ` [PATCH v5 3/9] phy: dphy: Clarify lanes parameter documentation Maxime Ripard
@ 2019-01-21 15:45 ` Maxime Ripard
  2019-02-07  8:15   ` Paul Kocialkowski
  2019-01-21 15:45 ` [PATCH v5 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-21 15:45 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,
	Maarten Lankhorst, Sean Paul, 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 v5 5/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/
  2019-01-21 15:45 [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (3 preceding siblings ...)
  2019-01-21 15:45 ` [PATCH v5 4/9] sun6i: dsi: Convert to generic phy handling Maxime Ripard
@ 2019-01-21 15:45 ` Maxime Ripard
  2019-02-07  8:19   ` Paul Kocialkowski
  2019-01-21 15:45 ` [PATCH v5 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Maxime Ripard @ 2019-01-21 15:45 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,
	Maarten Lankhorst, Sean Paul, 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 v5 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration
  2019-01-21 15:45 [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (4 preceding siblings ...)
  2019-01-21 15:45 ` [PATCH v5 5/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/ Maxime Ripard
@ 2019-01-21 15:45 ` Maxime Ripard
  2019-02-07  8:44   ` Paul Kocialkowski
  2019-01-21 15:45 ` [PATCH v5 7/9] dt-bindings: phy: Move the Cadence D-PHY bindings Maxime Ripard
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 21+ messages in thread
From: Maxime Ripard @ 2019-01-21 15:45 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,
	Maarten Lankhorst, Sean Paul, 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 | 101 ++++++++++++++++++++++---------
 1 file changed, 73 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
index ce9496d13986..796874e76308 100644
--- a/drivers/gpu/drm/bridge/cdns-dsi.c
+++ b/drivers/gpu/drm/bridge/cdns-dsi.c
@@ -545,6 +545,15 @@ 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,
+				    bool mode_valid_check)
+{
+	if (mode_valid_check)
+		return mode->hsync_start - mode->hdisplay;
+
+	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 +740,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 +764,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 +773,91 @@ 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, mode_valid_check),
+					 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_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_cfg->hfp = dpi_to_dsi_timing(dpi_hfp, bpp, DSI_HFP_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, mode_valid_check) * nlanes <
 	    (u64)dsi_hss_hsa_hse_hbp *
 	    (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000)
 		return -EINVAL;
@@ -842,7 +889,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 +907,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 +1035,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 v5 7/9] dt-bindings: phy: Move the Cadence D-PHY bindings
  2019-01-21 15:45 [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (5 preceding siblings ...)
  2019-01-21 15:45 ` [PATCH v5 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
@ 2019-01-21 15:45 ` Maxime Ripard
  2019-01-21 15:45 ` [PATCH v5 8/9] phy: Add Cadence D-PHY support Maxime Ripard
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-21 15:45 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,
	Maarten Lankhorst, Sean Paul, Maxime Ripard, Sakari Ailus

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.

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
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 v5 8/9] phy: Add Cadence D-PHY support
  2019-01-21 15:45 [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (6 preceding siblings ...)
  2019-01-21 15:45 ` [PATCH v5 7/9] dt-bindings: phy: Move the Cadence D-PHY bindings Maxime Ripard
@ 2019-01-21 15:45 ` Maxime Ripard
  2019-01-21 15:45 ` [PATCH v5 9/9] drm/bridge: cdns: Convert to phy framework Maxime Ripard
  2019-02-04 10:03 ` [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Kishon Vijay Abraham I
  9 siblings, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-21 15:45 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,
	Maarten Lankhorst, Sean Paul, Maxime Ripard, Sakari Ailus

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.

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
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..cde12b3aa4d4
--- /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 v5 9/9] drm/bridge: cdns: Convert to phy framework
  2019-01-21 15:45 [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (7 preceding siblings ...)
  2019-01-21 15:45 ` [PATCH v5 8/9] phy: Add Cadence D-PHY support Maxime Ripard
@ 2019-01-21 15:45 ` Maxime Ripard
  2019-02-04 10:03 ` [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Kishon Vijay Abraham I
  9 siblings, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-01-21 15:45 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,
	Maarten Lankhorst, Sean Paul, Maxime Ripard, Sean Paul

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.

Acked-by: Sean Paul <sean@poorly.run>
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/gpu/drm/bridge/Kconfig    |   1 +-
 drivers/gpu/drm/bridge/cdns-dsi.c | 485 +++----------------------------
 2 files changed, 60 insertions(+), 426 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 796874e76308..713a70b86046 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)
@@ -554,175 +488,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)
@@ -784,17 +549,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)
@@ -803,25 +571,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;
 
@@ -831,10 +601,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;
@@ -843,7 +613,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;
 
@@ -856,7 +634,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 *
+	if ((u64)phy_cfg->hs_clk_rate *
 	    mode_to_dpi_hfp(mode, mode_valid_check) * nlanes <
 	    (u64)dsi_hss_hsa_hse_hbp *
 	    (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000)
@@ -887,7 +665,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;
 
@@ -907,7 +684,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;
 
@@ -930,9 +707,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;
 
 	/*
@@ -943,30 +720,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);
@@ -976,7 +733,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);
 }
 
@@ -1022,7 +779,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;
@@ -1035,9 +792,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),
@@ -1073,9 +830,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);
 
@@ -1389,8 +1145,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;
 }
@@ -1399,8 +1153,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);
@@ -1411,121 +1163,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;
@@ -1564,13 +1201,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) {
@@ -1628,9 +1265,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;
 }
 
@@ -1640,7 +1274,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;
 }
-- 
git-series 0.9.1

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

* Re: [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices
  2019-01-21 15:45 [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (8 preceding siblings ...)
  2019-01-21 15:45 ` [PATCH v5 9/9] drm/bridge: cdns: Convert to phy framework Maxime Ripard
@ 2019-02-04 10:03 ` Kishon Vijay Abraham I
  2019-02-04 13:51   ` Maxime Ripard
  2019-02-05  8:46   ` Daniel Vetter
  9 siblings, 2 replies; 21+ messages in thread
From: Kishon Vijay Abraham I @ 2019-02-04 10:03 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,
	Maarten Lankhorst, Sean Paul



On 21/01/19 9:15 PM, 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.

Can the PHY changes go independently of the consumer drivers? or else I'll need
ACKs from the GPU MAINTAINER.

Thanks
Kishon

> 
> Let me know what you think,
> Maxime
> 
> Changes from v4:
>   - Removed regression on the variable calculation
>   - Fixed the wakeup unit
>   - Collected Sean Acked-by on the last patch
>   - Collected Sakari Reviewed-by on the first patch
> 
> 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                             | 538 +------
>  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, 894 insertions(+), 789 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
> 

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

* Re: [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices
  2019-02-04 10:03 ` [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Kishon Vijay Abraham I
@ 2019-02-04 13:51   ` Maxime Ripard
  2019-02-05  8:46   ` Daniel Vetter
  1 sibling, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-02-04 13:51 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,
	Maarten Lankhorst, Sean Paul

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

Hi Kishon,

On Mon, Feb 04, 2019 at 03:33:31PM +0530, Kishon Vijay Abraham I wrote:
> On 21/01/19 9:15 PM, Maxime Ripard wrote:
> > 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.
> 
> Can the PHY changes go independently of the consumer drivers? or else I'll need
> ACKs from the GPU MAINTAINER.

At least for the Allwinner driver, they can go through through the
drm-misc tree. Since we have a number of patches in flight for that
driver, it would even be easier to handle there.

For the cadence driver, since it doesn't really work on any system but
simulators for now, I guess the wakeup regression isn't super
important either.

So I'd say we can have the phy related patches go through your tree,
and the other through drm-misc.

Would that work for you?

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 v5 0/9] phy: Add configuration interface for MIPI D-PHY devices
  2019-02-04 10:03 ` [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Kishon Vijay Abraham I
  2019-02-04 13:51   ` Maxime Ripard
@ 2019-02-05  8:46   ` Daniel Vetter
  2019-02-06 12:13     ` Kishon Vijay Abraham I
  1 sibling, 1 reply; 21+ messages in thread
From: Daniel Vetter @ 2019-02-05  8:46 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Maxime Ripard, Rafal Ciepiela, Krzysztof Witos, linux-kernel,
	dri-devel, Chen-Yu Tsai, Sean Paul, Laurent Pinchart,
	Thomas Petazzoni, linux-arm-kernel, linux-media

On Mon, Feb 04, 2019 at 03:33:31PM +0530, Kishon Vijay Abraham I wrote:
> 
> 
> On 21/01/19 9:15 PM, 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.
> 
> Can the PHY changes go independently of the consumer drivers? or else I'll need
> ACKs from the GPU MAINTAINER.

Maxime is a gpu maintainer, so you're all good :-)
-Daniel

> 
> Thanks
> Kishon
> 
> > 
> > Let me know what you think,
> > Maxime
> > 
> > Changes from v4:
> >   - Removed regression on the variable calculation
> >   - Fixed the wakeup unit
> >   - Collected Sean Acked-by on the last patch
> >   - Collected Sakari Reviewed-by on the first patch
> > 
> > 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                             | 538 +------
> >  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, 894 insertions(+), 789 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
> > 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch

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

* Re: [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices
  2019-02-05  8:46   ` Daniel Vetter
@ 2019-02-06 12:13     ` Kishon Vijay Abraham I
  2019-02-06 12:25       ` Maxime Ripard
  0 siblings, 1 reply; 21+ messages in thread
From: Kishon Vijay Abraham I @ 2019-02-06 12:13 UTC (permalink / raw)
  To: Maxime Ripard, Rafal Ciepiela, Krzysztof Witos, linux-kernel,
	dri-devel, Chen-Yu Tsai, Sean Paul, Laurent Pinchart,
	Thomas Petazzoni, linux-arm-kernel, linux-media

Hi,

On 05/02/19 2:16 PM, Daniel Vetter wrote:
> On Mon, Feb 04, 2019 at 03:33:31PM +0530, Kishon Vijay Abraham I wrote:
>>
>>
>> On 21/01/19 9:15 PM, 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.
>>
>> Can the PHY changes go independently of the consumer drivers? or else I'll need
>> ACKs from the GPU MAINTAINER.
> 
> Maxime is a gpu maintainer, so you're all good :-)

cool.. I've merged all the patches except drm/bridge.

Please see if everything looks okay once it shows up in phy -next (give a day)

Thanks
Kishon
> -Daniel
> 
>>
>> Thanks
>> Kishon
>>
>>>
>>> Let me know what you think,
>>> Maxime
>>>
>>> Changes from v4:
>>>   - Removed regression on the variable calculation
>>>   - Fixed the wakeup unit
>>>   - Collected Sean Acked-by on the last patch
>>>   - Collected Sakari Reviewed-by on the first patch
>>>
>>> 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                             | 538 +------
>>>  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, 894 insertions(+), 789 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
>>>
>> _______________________________________________
>> dri-devel mailing list
>> dri-devel@lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
> 

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

* Re: [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices
  2019-02-06 12:13     ` Kishon Vijay Abraham I
@ 2019-02-06 12:25       ` Maxime Ripard
  2019-02-06 12:30         ` Kishon Vijay Abraham I
  0 siblings, 1 reply; 21+ messages in thread
From: Maxime Ripard @ 2019-02-06 12:25 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Rafal Ciepiela, Krzysztof Witos, linux-kernel, dri-devel,
	Chen-Yu Tsai, Sean Paul, Laurent Pinchart, Thomas Petazzoni,
	linux-arm-kernel, linux-media

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

Hi Kishon,

On Wed, Feb 06, 2019 at 05:43:12PM +0530, Kishon Vijay Abraham I wrote:
> On 05/02/19 2:16 PM, Daniel Vetter wrote:
> > On Mon, Feb 04, 2019 at 03:33:31PM +0530, Kishon Vijay Abraham I wrote:
> >>
> >>
> >> On 21/01/19 9:15 PM, 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.
> >>
> >> Can the PHY changes go independently of the consumer drivers? or else I'll need
> >> ACKs from the GPU MAINTAINER.
> > 
> > Maxime is a gpu maintainer, so you're all good :-)
> 
> cool.. I've merged all the patches except drm/bridge.
> 
> Please see if everything looks okay once it shows up in phy -next (give a day)

Thanks!

If possible (and if that's still an option), it would be better if the
sun6i related patches (patches 4 and 5) would go through the DRM tree
(with your Acked-by of course).

We have a number of patches in flight that have a decent chance to
conflict with patch 4.

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 v5 0/9] phy: Add configuration interface for MIPI D-PHY devices
  2019-02-06 12:25       ` Maxime Ripard
@ 2019-02-06 12:30         ` Kishon Vijay Abraham I
  2019-02-07  9:17           ` Maxime Ripard
  0 siblings, 1 reply; 21+ messages in thread
From: Kishon Vijay Abraham I @ 2019-02-06 12:30 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Rafal Ciepiela, Krzysztof Witos, linux-kernel, dri-devel,
	Chen-Yu Tsai, Sean Paul, Laurent Pinchart, Thomas Petazzoni,
	linux-arm-kernel, linux-media

Hi,

On 06/02/19 5:55 PM, Maxime Ripard wrote:
> Hi Kishon,
> 
> On Wed, Feb 06, 2019 at 05:43:12PM +0530, Kishon Vijay Abraham I wrote:
>> On 05/02/19 2:16 PM, Daniel Vetter wrote:
>>> On Mon, Feb 04, 2019 at 03:33:31PM +0530, Kishon Vijay Abraham I wrote:
>>>>
>>>>
>>>> On 21/01/19 9:15 PM, 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.
>>>>
>>>> Can the PHY changes go independently of the consumer drivers? or else I'll need
>>>> ACKs from the GPU MAINTAINER.
>>>
>>> Maxime is a gpu maintainer, so you're all good :-)
>>
>> cool.. I've merged all the patches except drm/bridge.
>>
>> Please see if everything looks okay once it shows up in phy -next (give a day)
> 
> Thanks!
> 
> If possible (and if that's still an option), it would be better if the
> sun6i related patches (patches 4 and 5) would go through the DRM tree
> (with your Acked-by of course).
> 
> We have a number of patches in flight that have a decent chance to
> conflict with patch 4.

Sure. Dropped patches 4 and 5 from my tree.

Thanks
Kishon

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

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

Hi,

On Mon, 2019-01-21 at 16:45 +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

* Re: [PATCH v5 5/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/
  2019-01-21 15:45 ` [PATCH v5 5/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/ Maxime Ripard
@ 2019-02-07  8:19   ` Paul Kocialkowski
  0 siblings, 0 replies; 21+ messages in thread
From: Paul Kocialkowski @ 2019-02-07  8:19 UTC (permalink / raw)
  To: Maxime Ripard, Kishon Vijay Abraham I
  Cc: Rafal Ciepiela, Krzysztof Witos, linux-kernel, dri-devel,
	Chen-Yu Tsai, Sean Paul, Laurent Pinchart, Thomas Petazzoni,
	linux-arm-kernel, linux-media

Hi,

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

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

Cheers,

Paul

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


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

* Re: [PATCH v5 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration
  2019-01-21 15:45 ` [PATCH v5 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
@ 2019-02-07  8:44   ` Paul Kocialkowski
  2019-02-07  9:16     ` Maxime Ripard
  0 siblings, 1 reply; 21+ messages in thread
From: Paul Kocialkowski @ 2019-02-07  8:44 UTC (permalink / raw)
  To: Maxime Ripard, Kishon Vijay Abraham I
  Cc: Rafal Ciepiela, Krzysztof Witos, linux-kernel, dri-devel,
	Chen-Yu Tsai, Sean Paul, Laurent Pinchart, Thomas Petazzoni,
	linux-arm-kernel, linux-media

Hi,

On Mon, 2019-01-21 at 16:45 +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.

See below about a silly mistake when refactoring. Looks good otherwise,
so with that fixed:

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

> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>  drivers/gpu/drm/bridge/cdns-dsi.c | 101 ++++++++++++++++++++++---------
>  1 file changed, 73 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
> index ce9496d13986..796874e76308 100644
> --- a/drivers/gpu/drm/bridge/cdns-dsi.c
> +++ b/drivers/gpu/drm/bridge/cdns-dsi.c
> @@ -545,6 +545,15 @@ 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,
> +				    bool mode_valid_check)
> +{
> +	if (mode_valid_check)
> +		return mode->hsync_start - mode->hdisplay;
> +
> +	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 +740,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 +764,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 +773,91 @@ 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, mode_valid_check),
> +					 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_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_cfg->hfp = dpi_to_dsi_timing(dpi_hfp, bpp, DSI_HFP_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),

The bpp argument sits between htotal and clock, this puts it after
clock which looks incorrect.

Cheers,

Paul

> +						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, mode_valid_check) * nlanes <
>  	    (u64)dsi_hss_hsa_hse_hbp *
>  	    (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000)
>  		return -EINVAL;
> @@ -842,7 +889,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 +907,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 +1035,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);
-- 
Paul Kocialkowski, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com


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

* Re: [PATCH v5 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration
  2019-02-07  8:44   ` Paul Kocialkowski
@ 2019-02-07  9:16     ` Maxime Ripard
  0 siblings, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-02-07  9:16 UTC (permalink / raw)
  To: Paul Kocialkowski
  Cc: Kishon Vijay Abraham I, Rafal Ciepiela, Krzysztof Witos,
	linux-kernel, dri-devel, Chen-Yu Tsai, Sean Paul,
	Laurent Pinchart, Thomas Petazzoni, linux-arm-kernel,
	linux-media

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

On Thu, Feb 07, 2019 at 09:44:46AM +0100, Paul Kocialkowski wrote:
> Hi,
> 
> On Mon, 2019-01-21 at 16:45 +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.
> 
> See below about a silly mistake when refactoring. Looks good otherwise,
> so with that fixed:
> 
> Reviewed-by: Paul Kocialkowski <paul.kocialkowski@bootlin.com>

I've fixed it while applying, 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 v5 0/9] phy: Add configuration interface for MIPI D-PHY devices
  2019-02-06 12:30         ` Kishon Vijay Abraham I
@ 2019-02-07  9:17           ` Maxime Ripard
  0 siblings, 0 replies; 21+ messages in thread
From: Maxime Ripard @ 2019-02-07  9:17 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Rafal Ciepiela, Krzysztof Witos, linux-kernel, dri-devel,
	Chen-Yu Tsai, Sean Paul, Laurent Pinchart, Thomas Petazzoni,
	linux-arm-kernel, linux-media

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

Hi Kishon,

On Wed, Feb 06, 2019 at 06:00:19PM +0530, Kishon Vijay Abraham I wrote:
> On 06/02/19 5:55 PM, Maxime Ripard wrote:
> > On Wed, Feb 06, 2019 at 05:43:12PM +0530, Kishon Vijay Abraham I wrote:
> >> On 05/02/19 2:16 PM, Daniel Vetter wrote:
> >>> On Mon, Feb 04, 2019 at 03:33:31PM +0530, Kishon Vijay Abraham I wrote:
> >>>>
> >>>>
> >>>> On 21/01/19 9:15 PM, 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.
> >>>>
> >>>> Can the PHY changes go independently of the consumer drivers? or else I'll need
> >>>> ACKs from the GPU MAINTAINER.
> >>>
> >>> Maxime is a gpu maintainer, so you're all good :-)
> >>
> >> cool.. I've merged all the patches except drm/bridge.
> >>
> >> Please see if everything looks okay once it shows up in phy -next (give a day)
> > 
> > Thanks!
> > 
> > If possible (and if that's still an option), it would be better if the
> > sun6i related patches (patches 4 and 5) would go through the DRM tree
> > (with your Acked-by of course).
> > 
> > We have a number of patches in flight that have a decent chance to
> > conflict with patch 4.
> 
> Sure. Dropped patches 4 and 5 from my tree.

Thanks! I've pushed the rest into drm-misc.

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

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

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-21 15:45 [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
2019-01-21 15:45 ` [PATCH v5 1/9] phy: dphy: Remove unused header Maxime Ripard
2019-01-21 15:45 ` [PATCH v5 2/9] phy: dphy: Change units of wakeup and init parameters Maxime Ripard
2019-01-21 15:45 ` [PATCH v5 3/9] phy: dphy: Clarify lanes parameter documentation Maxime Ripard
2019-01-21 15:45 ` [PATCH v5 4/9] sun6i: dsi: Convert to generic phy handling Maxime Ripard
2019-02-07  8:15   ` Paul Kocialkowski
2019-01-21 15:45 ` [PATCH v5 5/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/ Maxime Ripard
2019-02-07  8:19   ` Paul Kocialkowski
2019-01-21 15:45 ` [PATCH v5 6/9] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
2019-02-07  8:44   ` Paul Kocialkowski
2019-02-07  9:16     ` Maxime Ripard
2019-01-21 15:45 ` [PATCH v5 7/9] dt-bindings: phy: Move the Cadence D-PHY bindings Maxime Ripard
2019-01-21 15:45 ` [PATCH v5 8/9] phy: Add Cadence D-PHY support Maxime Ripard
2019-01-21 15:45 ` [PATCH v5 9/9] drm/bridge: cdns: Convert to phy framework Maxime Ripard
2019-02-04 10:03 ` [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices Kishon Vijay Abraham I
2019-02-04 13:51   ` Maxime Ripard
2019-02-05  8:46   ` Daniel Vetter
2019-02-06 12:13     ` Kishon Vijay Abraham I
2019-02-06 12:25       ` Maxime Ripard
2019-02-06 12:30         ` Kishon Vijay Abraham I
2019-02-07  9:17           ` Maxime Ripard

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