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

Hi,

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

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

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

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

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

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

Let me know what you think,
Maxime

Maxime Ripard (10):
  phy: Add MIPI D-PHY mode
  phy: Add configuration interface
  phy: Add MIPI D-PHY configuration options
  phy: dphy: Add configuration helpers
  sun6i: dsi: Convert to generic phy handling
  phy: Move Allwinner A31 D-PHY driver to drivers/phy/
  drm/bridge: cdns: Remove mode_check test
  drm/bridge: cdns: Separate DSI and D-PHY configuration
  phy: Add Cadence D-PHY support
  drm/bridge: cdns: Convert to phy framework

 drivers/gpu/drm/bridge/cdns-dsi.c           | 506 ++-------------------
 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      |  30 +-
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h      |  17 +-
 drivers/phy/Kconfig                         |   9 +-
 drivers/phy/Makefile                        |   2 +-
 drivers/phy/allwinner/Kconfig               |  12 +-
 drivers/phy/allwinner/Makefile              |   1 +-
 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 322 +++++++++++++-
 drivers/phy/cadence/Kconfig                 |  13 +-
 drivers/phy/cadence/Makefile                |   1 +-
 drivers/phy/cadence/cdns-dphy.c             | 499 +++++++++++++++++++++-
 drivers/phy/phy-core-mipi-dphy.c            | 160 +++++++-
 drivers/phy/phy-core.c                      |  62 +++-
 include/linux/phy/phy-mipi-dphy.h           | 247 ++++++++++-
 include/linux/phy/phy.h                     |  49 ++-
 18 files changed, 1455 insertions(+), 775 deletions(-)
 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/Kconfig
 create mode 100644 drivers/phy/cadence/Makefile
 create mode 100644 drivers/phy/cadence/cdns-dphy.c
 create mode 100644 drivers/phy/phy-core-mipi-dphy.c
 create mode 100644 include/linux/phy/phy-mipi-dphy.h

base-commit: 5b394b2ddf0347bef56e50c69a58773c94343ff3
-- 
git-series 0.9.1

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

* [PATCH 01/10] phy: Add MIPI D-PHY mode
  2018-09-05  9:16 [PATCH 00/10] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
@ 2018-09-05  9:16 ` Maxime Ripard
  2018-09-05 13:46   ` Laurent Pinchart
  2018-09-05  9:16 ` [PATCH 02/10] phy: Add configuration interface Maxime Ripard
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 40+ messages in thread
From: Maxime Ripard @ 2018-09-05  9:16 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Boris Brezillon
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

MIPI D-PHY is a MIPI standard meant mostly for display and cameras in
embedded systems. Add a mode for it.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 include/linux/phy/phy.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index 9713aebdd348..9cba7fe16c23 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -40,6 +40,7 @@ enum phy_mode {
 	PHY_MODE_10GKR,
 	PHY_MODE_UFS_HS_A,
 	PHY_MODE_UFS_HS_B,
+	PHY_MODE_MIPI_DPHY,
 };
 
 /**
-- 
git-series 0.9.1

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

* [PATCH 02/10] phy: Add configuration interface
  2018-09-05  9:16 [PATCH 00/10] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
  2018-09-05  9:16 ` [PATCH 01/10] phy: Add MIPI D-PHY mode Maxime Ripard
@ 2018-09-05  9:16 ` Maxime Ripard
  2018-09-05 13:39   ` Laurent Pinchart
  2018-09-06  9:27   ` Kishon Vijay Abraham I
  2018-09-05  9:16 ` [PATCH 03/10] phy: Add MIPI D-PHY configuration options Maxime Ripard
                   ` (7 subsequent siblings)
  9 siblings, 2 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-05  9:16 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Boris Brezillon
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

The phy framework is only allowing to configure the power state of the PHY
using the init and power_on hooks, and their power_off and exit
counterparts.

While it works for most, simple, PHYs supported so far, some more advanced
PHYs need some configuration depending on runtime parameters. These PHYs
have been supported by a number of means already, often by using ad-hoc
drivers in their consumer drivers.

That doesn't work too well however, when a consumer device needs to deal
multiple PHYs, or when multiple consumers need to deal with the same PHY (a
DSI driver and a CSI driver for example).

So we'll add a new interface, through two funtions, phy_validate and
phy_configure. The first one will allow to check that a current
configuration, for a given mode, is applicable. It will also allow the PHY
driver to tune the settings given as parameters as it sees fit.

phy_configure will actually apply that configuration in the phy itself.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/phy/phy-core.c  | 62 ++++++++++++++++++++++++++++++++++++++++++-
 include/linux/phy/phy.h | 42 ++++++++++++++++++++++++++++-
 2 files changed, 104 insertions(+)

diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index 35fd38c5a4a1..6eaf655e370f 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -408,6 +408,68 @@ int phy_calibrate(struct phy *phy)
 EXPORT_SYMBOL_GPL(phy_calibrate);
 
 /**
+ * phy_configure() - Changes the phy parameters
+ * @phy: the phy returned by phy_get()
+ * @mode: phy_mode the configuration is applicable to.
+ * @opts: New configuration to apply
+ *
+ * Used to change the PHY parameters. phy_init() must have
+ * been called on the phy.
+ *
+ * Returns: 0 if successful, an negative error code otherwise
+ */
+int phy_configure(struct phy *phy, enum phy_mode mode,
+		  union phy_configure_opts *opts)
+{
+	int ret;
+
+	if (!phy)
+		return -EINVAL;
+
+	if (!phy->ops->configure)
+		return 0;
+
+	mutex_lock(&phy->mutex);
+	ret = phy->ops->configure(phy, mode, opts);
+	mutex_unlock(&phy->mutex);
+
+	return ret;
+}
+
+/**
+ * phy_validate() - Checks the phy parameters
+ * @phy: the phy returned by phy_get()
+ * @mode: phy_mode the configuration is applicable to.
+ * @opts: Configuration to check
+ *
+ * Used to check that the current set of parameters can be handled by
+ * the phy. Implementations are free to tune the parameters passed as
+ * arguments if needed by some implementation detail or
+ * constraints. It will not change any actual configuration of the
+ * PHY, so calling it as many times as deemed fit will have no side
+ * effect.
+ *
+ * Returns: 0 if successful, an negative error code otherwise
+ */
+int phy_validate(struct phy *phy, enum phy_mode mode,
+		  union phy_configure_opts *opts)
+{
+	int ret;
+
+	if (!phy)
+		return -EINVAL;
+
+	if (!phy->ops->validate)
+		return 0;
+
+	mutex_lock(&phy->mutex);
+	ret = phy->ops->validate(phy, mode, opts);
+	mutex_unlock(&phy->mutex);
+
+	return ret;
+}
+
+/**
  * _of_phy_get() - lookup and obtain a reference to a phy by phandle
  * @np: device_node for which to get the phy
  * @index: the index of the phy
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index 9cba7fe16c23..3cc315dcfcd0 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -44,6 +44,12 @@ enum phy_mode {
 };
 
 /**
+ * union phy_configure_opts - Opaque generic phy configuration
+ */
+union phy_configure_opts {
+};
+
+/**
  * struct phy_ops - set of function pointers for performing phy operations
  * @init: operation to be performed for initializing phy
  * @exit: operation to be performed while exiting
@@ -60,6 +66,38 @@ struct phy_ops {
 	int	(*power_on)(struct phy *phy);
 	int	(*power_off)(struct phy *phy);
 	int	(*set_mode)(struct phy *phy, enum phy_mode mode);
+
+	/**
+	 * @configure:
+	 *
+	 * Optional.
+	 *
+	 * Used to change the PHY parameters. phy_init() must have
+	 * been called on the phy.
+	 *
+	 * Returns: 0 if successful, an negative error code otherwise
+	 */
+	int	(*configure)(struct phy *phy, enum phy_mode mode,
+			     union phy_configure_opts *opts);
+
+	/**
+	 * @validate:
+	 *
+	 * Optional.
+	 *
+	 * Used to check that the current set of parameters can be
+	 * handled by the phy. Implementations are free to tune the
+	 * parameters passed as arguments if needed by some
+	 * implementation detail or constraints. It must not change
+	 * any actual configuration of the PHY, so calling it as many
+	 * times as deemed fit by the consumer must have no side
+	 * effect.
+	 *
+	 * Returns: 0 if the configuration can be applied, an negative
+	 * error code otherwise
+	 */
+	int	(*validate)(struct phy *phy, enum phy_mode mode,
+			    union phy_configure_opts *opts);
 	int	(*reset)(struct phy *phy);
 	int	(*calibrate)(struct phy *phy);
 	struct module *owner;
@@ -164,6 +202,10 @@ int phy_exit(struct phy *phy);
 int phy_power_on(struct phy *phy);
 int phy_power_off(struct phy *phy);
 int phy_set_mode(struct phy *phy, enum phy_mode mode);
+int phy_configure(struct phy *phy, enum phy_mode mode,
+		  union phy_configure_opts *opts);
+int phy_validate(struct phy *phy, enum phy_mode mode,
+		 union phy_configure_opts *opts);
 static inline enum phy_mode phy_get_mode(struct phy *phy)
 {
 	return phy->attrs.mode;
-- 
git-series 0.9.1

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

* [PATCH 03/10] phy: Add MIPI D-PHY configuration options
  2018-09-05  9:16 [PATCH 00/10] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
  2018-09-05  9:16 ` [PATCH 01/10] phy: Add MIPI D-PHY mode Maxime Ripard
  2018-09-05  9:16 ` [PATCH 02/10] phy: Add configuration interface Maxime Ripard
@ 2018-09-05  9:16 ` Maxime Ripard
  2018-09-05 13:43   ` Laurent Pinchart
  2018-09-05  9:16 ` [PATCH 04/10] phy: dphy: Add configuration helpers Maxime Ripard
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 40+ messages in thread
From: Maxime Ripard @ 2018-09-05  9:16 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Boris Brezillon
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

Now that we have some infrastructure for it, allow the MIPI D-PHY phy's to
be configured through the generic functions through a custom structure
added to the generic union.

The parameters added here are the one defined in the MIPI D-PHY spec, plus
some parameters that were used by a number of PHY drivers currently found
in the linux kernel. The current set of parameters should cover all the
potential users.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 include/linux/phy/phy-mipi-dphy.h | 241 +++++++++++++++++++++++++++++++-
 include/linux/phy/phy.h           |   6 +-
 2 files changed, 247 insertions(+)
 create mode 100644 include/linux/phy/phy-mipi-dphy.h

diff --git a/include/linux/phy/phy-mipi-dphy.h b/include/linux/phy/phy-mipi-dphy.h
new file mode 100644
index 000000000000..792724145290
--- /dev/null
+++ b/include/linux/phy/phy-mipi-dphy.h
@@ -0,0 +1,241 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Cadence Design Systems Inc.
+ */
+
+#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
+ *
+ * This structure is used to represent the configuration state of a
+ * MIPI D-PHY phy.
+ */
+struct phy_configure_opts_mipi_dphy {
+	/**
+	 * @clk_miss:
+	 *
+	 * Timeout, in nanoseconds, for receiver to detect absence of
+	 * Clock transitions and disable the Clock Lane HS-RX.
+	 */
+	unsigned int		clk_miss;
+
+	/**
+	 * @clk_post:
+	 *
+	 * Time, in nanoseconds, that the transmitter continues to
+	 * send HS clock after the last associated Data Lane has
+	 * transitioned to LP Mode. Interval is defined as the period
+	 * from the end of @hs_trail to the beginning of @clk_trail.
+	 */
+	unsigned int		clk_post;
+
+	/**
+	 * @clk_pre:
+	 *
+	 * Time, in nanoseconds, that the HS clock shall be driven by
+	 * the transmitter prior to any associated Data Lane beginning
+	 * the transition from LP to HS mode.
+	 */
+	unsigned int		clk_pre;
+
+	/**
+	 * @clk_prepare:
+	 *
+	 * Time, in nanoseconds, that the transmitter drives the Clock
+	 * Lane LP-00 Line state immediately before the HS-0 Line
+	 * state starting the HS transmission.
+	 */
+	unsigned int		clk_prepare;
+
+	/**
+	 * @clk_settle:
+	 *
+	 * Time interval, in nanoseconds, during which the HS receiver
+	 * should ignore any Clock Lane HS transitions, starting from
+	 * the beginning of @clk_prepare.
+	 */
+	unsigned int		clk_settle;
+
+	/**
+	 * @clk_term_en:
+	 *
+	 * Time, in nanoseconds, for the Clock Lane receiver to enable
+	 * the HS line termination.
+	 */
+	unsigned int		clk_term_en;
+
+	/**
+	 * @clk_trail:
+	 *
+	 * Time, in nanoseconds, that the transmitter drives the HS-0
+	 * state after the last payload clock bit of a HS transmission
+	 * burst.
+	 */
+	unsigned int		clk_trail;
+
+	/**
+	 * @clk_zero:
+	 *
+	 * Time, in nanoseconds, that the transmitter drives the HS-0
+	 * state prior to starting the Clock.
+	 */
+	unsigned int		clk_zero;
+
+	/**
+	 * @d_term_en:
+	 *
+	 * Time, in nanoseconds, for the Data Lane receiver to enable
+	 * the HS line termination.
+	 */
+	unsigned int		d_term_en;
+
+	/**
+	 * @eot:
+	 *
+	 * Transmitted time interval, in nanoseconds, from the start
+	 * of @hs_trail or @clk_trail, to the start of the LP- 11
+	 * state following a HS burst.
+	 */
+	unsigned int		eot;
+
+	/**
+	 * @hs_exit:
+	 *
+	 * Time, in nanoseconds, that the transmitter drives LP-11
+	 * following a HS burst.
+	 */
+	unsigned int		hs_exit;
+
+	/**
+	 * @hs_prepare:
+	 *
+	 * Time, in nanoseconds, that the transmitter drives the Data
+	 * Lane LP-00 Line state immediately before the HS-0 Line
+	 * state starting the HS transmission
+	 */
+	unsigned int		hs_prepare;
+
+	/**
+	 * @hs_settle:
+	 *
+	 * Time interval, in nanoseconds, during which the HS receiver
+	 * shall ignore any Data Lane HS transitions, starting from
+	 * the beginning of @hs_prepare.
+	 */
+	unsigned int		hs_settle;
+
+	/**
+	 * @hs_skip:
+	 *
+	 * Time interval, in nanoseconds, during which the HS-RX
+	 * should ignore any transitions on the Data Lane, following a
+	 * HS burst. The end point of the interval is defined as the
+	 * beginning of the LP-11 state following the HS burst.
+	 */
+	unsigned int		hs_skip;
+
+	/**
+	 * @hs_trail:
+	 *
+	 * Time, in nanoseconds, that the transmitter drives the
+	 * flipped differential state after last payload data bit of a
+	 * HS transmission burst
+	 */
+	unsigned int		hs_trail;
+
+	/**
+	 * @hs_zero:
+	 *
+	 * Time, in nanoseconds, that the transmitter drives the HS-0
+	 * state prior to transmitting the Sync sequence.
+	 */
+	unsigned int		hs_zero;
+
+	/**
+	 * @lpx:
+	 *
+	 * Transmitted length, in nanoseconds, of any Low-Power state
+	 * period.
+	 */
+	unsigned int		lpx;
+
+	/**
+	 * @ta_get:
+	 *
+	 * Time, in nanoseconds, that the new transmitter drives the
+	 * Bridge state (LP-00) after accepting control during a Link
+	 * Turnaround.
+	 */
+	unsigned int		ta_get;
+
+	/**
+	 * @ta_go:
+	 *
+	 * Time, in nanoseconds, that the transmitter drives the
+	 * Bridge state (LP-00) before releasing control during a Link
+	 * Turnaround.
+	 */
+	unsigned int		ta_go;
+
+	/**
+	 * @ta_sure:
+	 *
+	 * Time, in nanoseconds, that the new transmitter waits after
+	 * the LP-10 state before transmitting the Bridge state
+	 * (LP-00) during a Link Turnaround.
+	 */
+	unsigned int		ta_sure;
+
+	/**
+	 * @wakeup:
+	 *
+	 * Time, in nanoseconds, that a transmitter drives a Mark-1
+	 * state prior to a Stop state in order to initiate an exit
+	 * from ULPS.
+	 */
+	unsigned int		wakeup;
+
+	/**
+	 * @hs_clk_rate:
+	 *
+	 * Clock rate, in Hertz, of the high-speed clock.
+	 */
+	unsigned long		hs_clk_rate;
+
+	/**
+	 * @lp_clk_rate:
+	 *
+	 * Clock rate, in Hertz, of the low-power clock.
+	 */
+	unsigned long		lp_clk_rate;
+
+	/**
+	 * @lanes:
+	 *
+	 * Number of lanes used for the transmissions.
+	 */
+	unsigned char		lanes;
+
+	/**
+	 * @modes:
+	 *
+	 * transmission operation mode flags
+	 */
+	u32			modes;
+
+	/**
+	 * @timings:
+	 *
+	 * Video timings associated with the transmission.
+	 */
+	struct videomode	timings;
+};
+
+/* TODO: Add other modes (burst, commands, etc) */
+#define MIPI_DPHY_MODE_VIDEO_SYNC_PULSE		BIT(0)
+
+#endif /* __PHY_MIPI_DPHY_H_ */
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index 3cc315dcfcd0..2b7a72f98428 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -20,6 +20,8 @@
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 
+#include <linux/phy/phy-mipi-dphy.h>
+
 struct phy;
 
 enum phy_mode {
@@ -45,8 +47,12 @@ enum phy_mode {
 
 /**
  * union phy_configure_opts - Opaque generic phy configuration
+ *
+ * @mipi_dphy:	Configuration set applicable for phys supporting
+ *		the MIPI_DPHY phy mode.
  */
 union phy_configure_opts {
+	struct phy_configure_opts_mipi_dphy	mipi_dphy;
 };
 
 /**
-- 
git-series 0.9.1

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

* [PATCH 04/10] phy: dphy: Add configuration helpers
  2018-09-05  9:16 [PATCH 00/10] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (2 preceding siblings ...)
  2018-09-05  9:16 ` [PATCH 03/10] phy: Add MIPI D-PHY configuration options Maxime Ripard
@ 2018-09-05  9:16 ` Maxime Ripard
  2018-09-05 13:46   ` Laurent Pinchart
  2018-09-05  9:16 ` [PATCH 05/10] sun6i: dsi: Convert to generic phy handling Maxime Ripard
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 40+ messages in thread
From: Maxime Ripard @ 2018-09-05  9:16 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Boris Brezillon
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

The MIPI D-PHY spec defines default values and boundaries for most of the
parameters it defines. Introduce helpers to help drivers get meaningful
values based on their current parameters, and validate the boundaries of
these parameters if needed.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/phy/Kconfig               |   8 ++-
 drivers/phy/Makefile              |   1 +-
 drivers/phy/phy-core-mipi-dphy.c  | 160 +++++++++++++++++++++++++++++++-
 include/linux/phy/phy-mipi-dphy.h |   6 +-
 4 files changed, 175 insertions(+)
 create mode 100644 drivers/phy/phy-core-mipi-dphy.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 5c8d452e35e2..06bd22bd1f4a 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -15,6 +15,14 @@ config GENERIC_PHY
 	  phy users can obtain reference to the PHY. All the users of this
 	  framework should select this config.
 
+config GENERIC_PHY_MIPI_DPHY
+	bool "MIPI D-PHY support"
+	help
+	  Generic MIPI D-PHY support.
+
+	  Provides a number of helpers a core functions for MIPI D-PHY
+	  drivers to us.
+
 config PHY_LPC18XX_USB_OTG
 	tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver"
 	depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 84e3bd9c5665..71c29d2b9af7 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -4,6 +4,7 @@
 #
 
 obj-$(CONFIG_GENERIC_PHY)		+= phy-core.o
+obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY)	+= phy-core-mipi-dphy.o
 obj-$(CONFIG_PHY_LPC18XX_USB_OTG)	+= phy-lpc18xx-usb-otg.o
 obj-$(CONFIG_PHY_XGENE)			+= phy-xgene.o
 obj-$(CONFIG_PHY_PISTACHIO_USB)		+= phy-pistachio-usb.o
diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c
new file mode 100644
index 000000000000..6c1ddc7734a2
--- /dev/null
+++ b/drivers/phy/phy-core-mipi-dphy.c
@@ -0,0 +1,160 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2013 NVIDIA Corporation
+ * Copyright (C) 2018 Cadence Design Systems Inc.
+ */
+
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/time64.h>
+
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+
+/*
+ * Default D-PHY timings based on MIPI D-PHY specification. Derived from the
+ * valid ranges specified in Section 6.9, Table 14, Page 40 of the D-PHY
+ * specification (v1.2) with minor adjustments.
+ */
+int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
+				     unsigned int bpp,
+				     unsigned int lanes,
+				     struct phy_configure_opts_mipi_dphy *cfg)
+{
+	unsigned long hs_clk_rate;
+	unsigned long ui;
+
+	if (!cfg)
+		return -EINVAL;
+
+	hs_clk_rate = pixel_clock * bpp / lanes;
+	ui = DIV_ROUND_UP(NSEC_PER_SEC, hs_clk_rate);
+
+	cfg->clk_miss = 0;
+	cfg->clk_post = 70 + 52 * ui;
+	cfg->clk_pre = 8;
+	cfg->clk_prepare = 65;
+	cfg->clk_settle = 95;
+	cfg->clk_term_en = 0;
+	cfg->clk_trail = 80;
+	cfg->clk_zero = 260;
+	cfg->d_term_en = 0;
+	cfg->eot = 0;
+	cfg->hs_exit = 120;
+	cfg->hs_prepare = 65 + 5 * ui;
+	cfg->hs_zero = 145 + 5 * ui;
+	cfg->hs_settle = 85 + 6 * ui;
+	cfg->hs_skip = 40;
+
+	/*
+	 * The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40)
+	 * contains this formula as:
+	 *
+	 *     T_HS-TRAIL = max(n * 8 * ui, 60 + n * 4 * ui)
+	 *
+	 * where n = 1 for forward-direction HS mode and n = 4 for reverse-
+	 * direction HS mode. There's only one setting and this function does
+	 * not parameterize on anything other that ui, so this code will
+	 * assumes that reverse-direction HS mode is supported and uses n = 4.
+	 */
+	cfg->hs_trail = max(4 * 8 * ui, 60 + 4 * 4 * ui);
+
+	cfg->init = 100000;
+	cfg->lpx = 60;
+	cfg->ta_get = 5 * cfg->lpx;
+	cfg->ta_go = 4 * cfg->lpx;
+	cfg->ta_sure = 2 * cfg->lpx;
+	cfg->wakeup = 1000000;
+
+	cfg->hs_clk_rate = hs_clk_rate;
+	cfg->lanes = lanes;
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_mipi_dphy_get_default_config);
+
+/*
+ * Validate D-PHY configuration according to MIPI D-PHY specification
+ * (v1.2, Section Section 6.9 "Global Operation Timing Parameters").
+ */
+int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
+{
+	unsigned long ui;
+
+	if (!cfg)
+		return -EINVAL;
+
+	ui = DIV_ROUND_UP(NSEC_PER_SEC, cfg->hs_clk_rate);
+
+	if (cfg->clk_miss > 60)
+		return -EINVAL;
+
+	if (cfg->clk_post < (60 + 52 * ui))
+		return -EINVAL;
+
+	if (cfg->clk_pre < 8)
+		return -EINVAL;
+
+	if (cfg->clk_prepare < 38 || cfg->clk_prepare > 95)
+		return -EINVAL;
+
+	if (cfg->clk_settle < 95 || cfg->clk_settle > 300)
+		return -EINVAL;
+
+	if (cfg->clk_term_en > 38)
+		return -EINVAL;
+
+	if (cfg->clk_trail < 60)
+		return -EINVAL;
+
+	if (cfg->clk_prepare + cfg->clk_zero < 300)
+		return -EINVAL;
+
+	if (cfg->d_term_en > 35 + 4 * ui)
+		return -EINVAL;
+
+	if (cfg->eot > 105 + 12 * ui)
+		return -EINVAL;
+
+	if (cfg->hs_exit < 100)
+		return -EINVAL;
+
+	if (cfg->hs_prepare < 40 + 4 * ui ||
+	    cfg->hs_prepare > 85 + 6 * ui)
+		return -EINVAL;
+
+	if (cfg->hs_prepare + cfg->hs_zero < 145 + 10 * ui)
+		return -EINVAL;
+
+	if ((cfg->hs_settle < 85 + 6 * ui) ||
+	    (cfg->hs_settle > 145 + 10 * ui))
+		return -EINVAL;
+
+	if (cfg->hs_skip < 40 || cfg->hs_skip > 55 + 4 * ui)
+		return -EINVAL;
+
+	if (cfg->hs_trail < max(8 * ui, 60 + 4 * ui))
+		return -EINVAL;
+
+	if (cfg->init < 100000)
+		return -EINVAL;
+
+	if (cfg->lpx < 50)
+		return -EINVAL;
+
+	if (cfg->ta_get != 5 * cfg->lpx)
+		return -EINVAL;
+
+	if (cfg->ta_go != 4 * cfg->lpx)
+		return -EINVAL;
+
+	if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > 2 * cfg->lpx)
+		return -EINVAL;
+
+	if (cfg->wakeup < 1000000)
+		return -EINVAL;
+
+	return 0;
+}
+EXPORT_SYMBOL(phy_mipi_dphy_config_validate);
diff --git a/include/linux/phy/phy-mipi-dphy.h b/include/linux/phy/phy-mipi-dphy.h
index 792724145290..7656d057198f 100644
--- a/include/linux/phy/phy-mipi-dphy.h
+++ b/include/linux/phy/phy-mipi-dphy.h
@@ -238,4 +238,10 @@ struct phy_configure_opts_mipi_dphy {
 /* TODO: Add other modes (burst, commands, etc) */
 #define MIPI_DPHY_MODE_VIDEO_SYNC_PULSE		BIT(0)
 
+int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
+				     unsigned int bpp,
+				     unsigned int lanes,
+				     struct phy_configure_opts_mipi_dphy *cfg);
+int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg);
+
 #endif /* __PHY_MIPI_DPHY_H_ */
-- 
git-series 0.9.1

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

* [PATCH 05/10] sun6i: dsi: Convert to generic phy handling
  2018-09-05  9:16 [PATCH 00/10] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (3 preceding siblings ...)
  2018-09-05  9:16 ` [PATCH 04/10] phy: dphy: Add configuration helpers Maxime Ripard
@ 2018-09-05  9:16 ` Maxime Ripard
  2018-09-05  9:16 ` [PATCH 06/10] phy: Move Allwinner A31 D-PHY driver to drivers/phy/ Maxime Ripard
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-05  9:16 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Boris Brezillon
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

Now that we have everything in place in the PHY framework to deal in a
generic way with MIPI D-PHY phys, let's convert our PHY driver and its
associated DSI driver to that new API.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/gpu/drm/sun4i/Kconfig           |  11 +-
 drivers/gpu/drm/sun4i/Makefile          |   6 +-
 drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c | 168 ++++++++++++++-----------
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c  |  30 ++--
 drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h  |  17 +---
 5 files changed, 129 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..37b340044fe1 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,50 @@
 
 #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, enum phy_mode mode,
+				union phy_configure_opts *opts)
+{
+	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+	int ret;
+
+	if (mode != PHY_MODE_MIPI_DPHY)
+		return -EINVAL;
+
+	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 +152,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 +215,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 +236,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 +253,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..4bde604371aa 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,14 @@ 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_configure(dsi->dphy, PHY_MODE_MIPI_DPHY, &opts);
+	phy_power_on(dsi->dphy);
 
 	if (!IS_ERR(dsi->panel))
 		drm_panel_prepare(dsi->panel);
@@ -673,8 +682,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 +976,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 +1021,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 +1032,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 +1045,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 +1060,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] 40+ messages in thread

* [PATCH 06/10] phy: Move Allwinner A31 D-PHY driver to drivers/phy/
  2018-09-05  9:16 [PATCH 00/10] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (4 preceding siblings ...)
  2018-09-05  9:16 ` [PATCH 05/10] sun6i: dsi: Convert to generic phy handling Maxime Ripard
@ 2018-09-05  9:16 ` Maxime Ripard
  2018-09-05  9:16 ` [PATCH 07/10] drm/bridge: cdns: Remove mode_check test Maxime Ripard
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-05  9:16 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Boris Brezillon
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

Now that our MIPI D-PHY driver has been converted to the phy framework,
let's move it into the drivers/phy directory.

Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
---
 drivers/gpu/drm/sun4i/Kconfig               |  10 +-
 drivers/gpu/drm/sun4i/Makefile              |   1 +-
 drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c     | 322 +---------------------
 drivers/phy/allwinner/Kconfig               |  12 +-
 drivers/phy/allwinner/Makefile              |   1 +-
 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 322 +++++++++++++++++++++-
 6 files changed, 336 insertions(+), 332 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 37b340044fe1..000000000000
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
+++ /dev/null
@@ -1,322 +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, enum phy_mode mode,
-				union phy_configure_opts *opts)
-{
-	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
-	int ret;
-
-	if (mode != PHY_MODE_MIPI_DPHY)
-		return -EINVAL;
-
-	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..37b340044fe1
--- /dev/null
+++ b/drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
@@ -0,0 +1,322 @@
+// 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, enum phy_mode mode,
+				union phy_configure_opts *opts)
+{
+	struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+	int ret;
+
+	if (mode != PHY_MODE_MIPI_DPHY)
+		return -EINVAL;
+
+	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] 40+ messages in thread

* [PATCH 07/10] drm/bridge: cdns: Remove mode_check test
  2018-09-05  9:16 [PATCH 00/10] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (5 preceding siblings ...)
  2018-09-05  9:16 ` [PATCH 06/10] phy: Move Allwinner A31 D-PHY driver to drivers/phy/ Maxime Ripard
@ 2018-09-05  9:16 ` Maxime Ripard
  2018-09-05  9:16 ` [PATCH 08/10] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-05  9:16 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Boris Brezillon
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

The crtc_* variants of the timings are initialised at the same value than
their !crtc counterparts, and can later on be tweaked. This means that we
can always use the crtc variants, instead of trying to figure out which of
these two we should use.

Move to always use the crtc_* variants, and remove the extra parameter to
the mode2cfg function that allows to tell which variant we should use.

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

diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
index ce9496d13986..6474e6d999b7 100644
--- a/drivers/gpu/drm/bridge/cdns-dsi.c
+++ b/drivers/gpu/drm/bridge/cdns-dsi.c
@@ -731,8 +731,7 @@ 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)
+			     struct cdns_dphy_cfg *dphy_cfg)
 {
 	unsigned long dsi_htotal = 0, dsi_hss_hsa_hse_hbp = 0;
 	struct cdns_dsi_output *output = &dsi->output;
@@ -748,23 +747,15 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
 	bpp = mipi_dsi_pixel_format_to_bpp(output->dev->format);
 	nlanes = output->dev->lanes;
 
-	if (mode_valid_check)
-		tmp = mode->htotal -
-		      (sync_pulse ? mode->hsync_end : mode->hsync_start);
-	else
-		tmp = mode->crtc_htotal -
-		      (sync_pulse ?
-		       mode->crtc_hsync_end : mode->crtc_hsync_start);
+	tmp = mode->crtc_htotal -
+		(sync_pulse ? 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)
-			tmp = mode->hsync_end - mode->hsync_start;
-		else
-			tmp = mode->crtc_hsync_end - mode->crtc_hsync_start;
+		tmp = mode->crtc_hsync_end - mode->crtc_hsync_start;
 
 		dsi_cfg->hsa = dpi_to_dsi_timing(tmp, bpp,
 						 DSI_HSA_FRAME_OVERHEAD);
@@ -772,32 +763,19 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
 		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,
+	dsi_cfg->hact = dpi_to_dsi_timing(mode->crtc_hdisplay,
 					  bpp, 0);
 	dsi_htotal += dsi_cfg->hact;
 
-	if (mode_valid_check)
-		dpi_hfp = mode->hsync_start - mode->hdisplay;
-	else
-		dpi_hfp = mode->crtc_hsync_start - mode->crtc_hdisplay;
-
+	dpi_hfp = mode->crtc_hsync_start - mode->crtc_hdisplay;
 	dsi_cfg->hfp = dpi_to_dsi_timing(dpi_hfp, bpp, DSI_HFP_FRAME_OVERHEAD);
 	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->clock * 1000,
-						dsi_htotal, nlanes,
-						&dsi_hfp_ext);
-	else
-		ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
-						mode->crtc_htotal, bpp,
-						mode->crtc_clock * 1000,
-						dsi_htotal, nlanes,
-						&dsi_hfp_ext);
-
+	ret = cdns_dsi_get_dphy_pll_cfg(dsi->dphy, dphy_cfg,
+					mode->crtc_htotal, bpp,
+					mode->crtc_clock * 1000,
+					dsi_htotal, nlanes,
+					&dsi_hfp_ext);
 	if (ret)
 		return ret;
 
@@ -811,8 +789,7 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
 	 * interface.
 	 */
 	if ((u64)dphy_cfg->lane_bps * dpi_hfp * nlanes <
-	    (u64)dsi_hss_hsa_hse_hbp *
-	    (mode_valid_check ? mode->clock : mode->crtc_clock) * 1000)
+	    (u64)dsi_hss_hsa_hse_hbp * mode->crtc_clock * 1000)
 		return -EINVAL;
 
 	return 0;
@@ -862,7 +839,7 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge,
 
 	nlanes = output->dev->lanes;
 
-	ret = cdns_dsi_mode2cfg(dsi, mode, &dsi_cfg, &dphy_cfg, true);
+	ret = cdns_dsi_mode2cfg(dsi, mode, &dsi_cfg, &dphy_cfg);
 	if (ret)
 		return MODE_CLOCK_RANGE;
 
@@ -990,7 +967,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_mode2cfg(dsi, mode, &dsi_cfg, &dphy_cfg));
 
 	cdns_dsi_hs_init(dsi, &dphy_cfg);
 	cdns_dsi_init_link(dsi);
-- 
git-series 0.9.1

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

* [PATCH 08/10] drm/bridge: cdns: Separate DSI and D-PHY configuration
  2018-09-05  9:16 [PATCH 00/10] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (6 preceding siblings ...)
  2018-09-05  9:16 ` [PATCH 07/10] drm/bridge: cdns: Remove mode_check test Maxime Ripard
@ 2018-09-05  9:16 ` Maxime Ripard
  2018-09-05  9:16 ` [PATCH 09/10] phy: Add Cadence D-PHY support Maxime Ripard
  2018-09-05  9:16 ` [PATCH 10/10] drm/bridge: cdns: Convert to phy framework Maxime Ripard
  9 siblings, 0 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-05  9:16 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Boris Brezillon
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

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

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

diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
index 6474e6d999b7..bb9a71ad3d91 100644
--- a/drivers/gpu/drm/bridge/cdns-dsi.c
+++ b/drivers/gpu/drm/bridge/cdns-dsi.c
@@ -545,6 +545,11 @@ bridge_to_cdns_dsi_input(struct drm_bridge *bridge)
 	return container_of(bridge, struct cdns_dsi_input, bridge);
 }
 
+static unsigned int mode_to_dpi_hfp(const struct drm_display_mode *mode)
+{
+	return mode->crtc_hsync_start - mode->crtc_hdisplay;
+}
+
 static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
 				     struct cdns_dphy_cfg *cfg,
 				     unsigned int dpi_htotal,
@@ -730,14 +735,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)
+			     struct cdns_dsi_cfg *dsi_cfg)
 {
-	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));
 
@@ -751,44 +754,84 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
 		(sync_pulse ? 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) {
 		tmp = mode->crtc_hsync_end - mode->crtc_hsync_start;
 
 		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->crtc_hdisplay,
 					  bpp, 0);
-	dsi_htotal += dsi_cfg->hact;
+	dsi_cfg->hfp = dpi_to_dsi_timing(mode_to_dpi_hfp(mode), bpp,
+					 DSI_HFP_FRAME_OVERHEAD);
+
+	return 0;
+}
 
-	dpi_hfp = mode->crtc_hsync_start - mode->crtc_hdisplay;
-	dsi_cfg->hfp = dpi_to_dsi_timing(dpi_hfp, bpp, DSI_HFP_FRAME_OVERHEAD);
+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)
+{
+	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_htotal += dsi_cfg->hact;
 	dsi_htotal += dsi_cfg->hfp + DSI_HFP_FRAME_OVERHEAD;
 
 	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)
+{
+	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);
+	if (ret)
+		return ret;
+
+	ret = cdns_dphy_validate(dsi, dsi_cfg, dphy_cfg, mode);
+	if (ret)
+		return ret;
+
+	dsi_hss_hsa_hse_hbp = dsi_cfg->hbp + DSI_HBP_FRAME_OVERHEAD;
+	if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+		dsi_hss_hsa_hse_hbp += dsi_cfg->hsa + DSI_HSA_FRAME_OVERHEAD;
 
 	/*
 	 * Make sure DPI(HFP) > DSI(HSS+HSA+HSE+HBP) to guarantee that the FIFO
 	 * is empty before we start a receiving a new line on the DPI
 	 * interface.
 	 */
-	if ((u64)dphy_cfg->lane_bps * dpi_hfp * nlanes <
+	if ((u64)dphy_cfg->lane_bps * mode_to_dpi_hfp(mode) * nlanes <
 	    (u64)dsi_hss_hsa_hse_hbp * mode->crtc_clock * 1000)
 		return -EINVAL;
 
@@ -819,7 +862,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
@@ -837,11 +880,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);
+	ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg);
 	if (ret)
-		return MODE_CLOCK_RANGE;
+		return MODE_BAD;
 
 	return MODE_OK;
 }
@@ -967,7 +1008,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));
+	WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, &dphy_cfg));
 
 	cdns_dsi_hs_init(dsi, &dphy_cfg);
 	cdns_dsi_init_link(dsi);
-- 
git-series 0.9.1

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

* [PATCH 09/10] phy: Add Cadence D-PHY support
  2018-09-05  9:16 [PATCH 00/10] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (7 preceding siblings ...)
  2018-09-05  9:16 ` [PATCH 08/10] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
@ 2018-09-05  9:16 ` Maxime Ripard
  2018-09-05 13:48   ` Laurent Pinchart
  2018-09-05  9:16 ` [PATCH 10/10] drm/bridge: cdns: Convert to phy framework Maxime Ripard
  9 siblings, 1 reply; 40+ messages in thread
From: Maxime Ripard @ 2018-09-05  9:16 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Boris Brezillon
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

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

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

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

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 06bd22bd1f4a..a65427fc3fbd 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -51,6 +51,7 @@ config PHY_XGENE
 source "drivers/phy/allwinner/Kconfig"
 source "drivers/phy/amlogic/Kconfig"
 source "drivers/phy/broadcom/Kconfig"
+source "drivers/phy/cadence/Kconfig"
 source "drivers/phy/hisilicon/Kconfig"
 source "drivers/phy/lantiq/Kconfig"
 source "drivers/phy/marvell/Kconfig"
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 71c29d2b9af7..9db9c33dcd3f 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_ARCH_RENESAS)		+= renesas/
 obj-$(CONFIG_ARCH_ROCKCHIP)		+= rockchip/
 obj-$(CONFIG_ARCH_TEGRA)		+= tegra/
 obj-y					+= broadcom/	\
+					   cadence/	\
 					   hisilicon/	\
 					   marvell/	\
 					   motorola/	\
diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
new file mode 100644
index 000000000000..fbad4466dd1d
--- /dev/null
+++ b/drivers/phy/cadence/Kconfig
@@ -0,0 +1,13 @@
+#
+# Phy drivers for Cadence IPs
+#
+
+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-csi.
diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile
new file mode 100644
index 000000000000..d7ada8d9d11b
--- /dev/null
+++ b/drivers/phy/cadence/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_PHY_CADENCE_DPHY)	+= cdns-dphy.o
diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c
new file mode 100644
index 000000000000..4d7f72f7e78d
--- /dev/null
+++ b/drivers/phy/cadence/cdns-dphy.c
@@ -0,0 +1,499 @@
+// 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 long lane_bps;
+	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)
+{
+	u64 dlane_bps, dlane_bps_max, fbdiv, fbdiv_max, adj_dsi_htotal;
+	unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk);
+	unsigned long dpi_htotal = opts->timings.hactive +
+		opts->timings.hfront_porch + opts->timings.hback_porch +
+		opts->timings.hsync_len;
+	unsigned long dsi_htotal = opts->timings.hactive +
+		opts->timings.hfront_porch + DSI_HFP_FRAME_OVERHEAD +
+		opts->timings.hback_porch + DSI_HBP_FRAME_OVERHEAD;
+
+	if (opts->modes & MIPI_DPHY_MODE_VIDEO_SYNC_PULSE)
+		dsi_htotal += opts->timings.hsync_len + DSI_HSA_FRAME_OVERHEAD;
+
+	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;
+
+	/*
+	 * 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 % opts->lanes)
+		adj_dsi_htotal += opts->lanes - (dsi_htotal % opts->lanes);
+
+	dlane_bps = (u64)opts->timings.pixelclock * adj_dsi_htotal;
+
+	/* data rate in bytes/sec is not an integer, refuse the mode. */
+	if (do_div(dlane_bps, opts->lanes * 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 * opts->lanes *
+				 dpi_htotal;
+
+		/*
+		 * Do the division in 2 steps to avoid an overflow on the
+		 * divider.
+		 */
+		rem = do_div(adj_dsi_htotal, opts->timings.pixelclock);
+		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)opts->timings.pixelclock * adj_dsi_htotal * 8,
+				       opts->lanes * 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 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->timings.hfront_porch += dsi_hfp_ext;
+	opts->timings.hsync_len += dsi_hfp_ext;
+	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy);
+
+	return 0;
+}
+
+static int cdns_dphy_init(struct phy *phy)
+{
+	struct cdns_dphy *dphy = phy_get_drvdata(phy);
+
+	clk_prepare_enable(dphy->psm_clk);
+	clk_prepare_enable(dphy->pll_ref_clk);
+
+	return 0;
+}
+
+static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode,
+			      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, enum phy_mode mode,
+				union phy_configure_opts *opts)
+{
+	struct cdns_dphy *dphy = phy_get_drvdata(phy);
+	struct cdns_dphy_cfg cfg = { 0 };
+	int ret;
+
+	if (mode != PHY_MODE_MIPI_DPHY)
+		return -EINVAL;
+
+	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);
+
+	/* 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_exit(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 struct phy_ops cdns_dphy_ops = {
+	.configure	= cdns_dphy_configure,
+	.validate	= cdns_dphy_validate,
+	.power_on	= cdns_dphy_power_on,
+	.init		= cdns_dphy_init,
+	.exit		= cdns_dphy_exit,
+};
+
+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");
+		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>");
+MODULE_DESCRIPTION("Cadence MIPI D-PHY Driver");
+MODULE_LICENSE("GPL");
-- 
git-series 0.9.1

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

* [PATCH 10/10] drm/bridge: cdns: Convert to phy framework
  2018-09-05  9:16 [PATCH 00/10] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
                   ` (8 preceding siblings ...)
  2018-09-05  9:16 ` [PATCH 09/10] phy: Add Cadence D-PHY support Maxime Ripard
@ 2018-09-05  9:16 ` Maxime Ripard
  9 siblings, 0 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-05  9:16 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, Boris Brezillon
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela, Maxime Ripard

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

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

diff --git a/drivers/gpu/drm/bridge/cdns-dsi.c b/drivers/gpu/drm/bridge/cdns-dsi.c
index bb9a71ad3d91..aaf3ce68e78c 100644
--- a/drivers/gpu/drm/bridge/cdns-dsi.c
+++ b/drivers/gpu/drm/bridge/cdns-dsi.c
@@ -21,6 +21,9 @@
 #include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
+
 #define IP_CONF				0x0
 #define SP_HS_FIFO_DEPTH(x)		(((x) & GENMASK(30, 26)) >> 26)
 #define SP_LP_FIFO_DEPTH(x)		(((x) & GENMASK(25, 21)) >> 21)
@@ -419,44 +422,11 @@
 #define DSI_NULL_FRAME_OVERHEAD		6
 #define DSI_EOT_PKT_SIZE		4
 
-#define REG_WAKEUP_TIME_NS		800
-#define DPHY_PLL_RATE_HZ		108000000
-
-/* DPHY registers */
-#define DPHY_PMA_CMN(reg)		(reg)
-#define DPHY_PMA_LCLK(reg)		(0x100 + (reg))
-#define DPHY_PMA_LDATA(lane, reg)	(0x200 + ((lane) * 0x100) + (reg))
-#define DPHY_PMA_RCLK(reg)		(0x600 + (reg))
-#define DPHY_PMA_RDATA(lane, reg)	(0x700 + ((lane) * 0x100) + (reg))
-#define DPHY_PCS(reg)			(0xb00 + (reg))
-
-#define DPHY_CMN_SSM			DPHY_PMA_CMN(0x20)
-#define DPHY_CMN_SSM_EN			BIT(0)
-#define DPHY_CMN_TX_MODE_EN		BIT(9)
-
-#define DPHY_CMN_PWM			DPHY_PMA_CMN(0x40)
-#define DPHY_CMN_PWM_DIV(x)		((x) << 20)
-#define DPHY_CMN_PWM_LOW(x)		((x) << 10)
-#define DPHY_CMN_PWM_HIGH(x)		(x)
-
-#define DPHY_CMN_FBDIV			DPHY_PMA_CMN(0x4c)
-#define DPHY_CMN_FBDIV_VAL(low, high)	(((high) << 11) | ((low) << 22))
-#define DPHY_CMN_FBDIV_FROM_REG		(BIT(10) | BIT(21))
-
-#define DPHY_CMN_OPIPDIV		DPHY_PMA_CMN(0x50)
-#define DPHY_CMN_IPDIV_FROM_REG		BIT(0)
-#define DPHY_CMN_IPDIV(x)		((x) << 1)
-#define DPHY_CMN_OPDIV_FROM_REG		BIT(6)
-#define DPHY_CMN_OPDIV(x)		((x) << 7)
-
-#define DPHY_PSM_CFG			DPHY_PCS(0x4)
-#define DPHY_PSM_CFG_FROM_REG		BIT(0)
-#define DPHY_PSM_CLK_DIV(x)		((x) << 1)
-
 struct cdns_dsi_output {
 	struct mipi_dsi_device *dev;
 	struct drm_panel *panel;
 	struct drm_bridge *bridge;
+	union phy_configure_opts phy_opts;
 };
 
 enum cdns_dsi_input_id {
@@ -465,14 +435,6 @@ enum cdns_dsi_input_id {
 	CDNS_DSC_INPUT,
 };
 
-struct cdns_dphy_cfg {
-	u8 pll_ipdiv;
-	u8 pll_opdiv;
-	u16 pll_fbdiv;
-	unsigned long lane_bps;
-	unsigned int nlanes;
-};
-
 struct cdns_dsi_cfg {
 	unsigned int hfp;
 	unsigned int hsa;
@@ -481,34 +443,6 @@ struct cdns_dsi_cfg {
 	unsigned int htotal;
 };
 
-struct cdns_dphy;
-
-enum cdns_dphy_clk_lane_cfg {
-	DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
-	DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
-	DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
-	DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
-};
-
-struct cdns_dphy_ops {
-	int (*probe)(struct cdns_dphy *dphy);
-	void (*remove)(struct cdns_dphy *dphy);
-	void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
-	void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
-				 enum cdns_dphy_clk_lane_cfg cfg);
-	void (*set_pll_cfg)(struct cdns_dphy *dphy,
-			    const struct cdns_dphy_cfg *cfg);
-	unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
-};
-
-struct cdns_dphy {
-	struct cdns_dphy_cfg cfg;
-	void __iomem *regs;
-	struct clk *psm_clk;
-	struct clk *pll_ref_clk;
-	const struct cdns_dphy_ops *ops;
-};
-
 struct cdns_dsi_input {
 	enum cdns_dsi_input_id id;
 	struct drm_bridge bridge;
@@ -526,7 +460,7 @@ struct cdns_dsi {
 	struct reset_control *dsi_p_rst;
 	struct clk *dsi_sys_clk;
 	bool link_initialized;
-	struct cdns_dphy *dphy;
+	struct phy *dphy;
 };
 
 static inline struct cdns_dsi *input_to_dsi(struct cdns_dsi_input *input)
@@ -550,175 +484,6 @@ static unsigned int mode_to_dpi_hfp(const struct drm_display_mode *mode)
 	return mode->crtc_hsync_start - mode->crtc_hdisplay;
 }
 
-static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
-				     struct cdns_dphy_cfg *cfg,
-				     unsigned int dpi_htotal,
-				     unsigned int dpi_bpp,
-				     unsigned int dpi_hz,
-				     unsigned int dsi_htotal,
-				     unsigned int dsi_nlanes,
-				     unsigned int *dsi_hfp_ext)
-{
-	u64 dlane_bps, dlane_bps_max, fbdiv, fbdiv_max, adj_dsi_htotal;
-	unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk);
-
-	memset(cfg, 0, sizeof(*cfg));
-
-	cfg->nlanes = dsi_nlanes;
-
-	if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
-		return -EINVAL;
-	else if (pll_ref_hz < 19200000)
-		cfg->pll_ipdiv = 1;
-	else if (pll_ref_hz < 38400000)
-		cfg->pll_ipdiv = 2;
-	else if (pll_ref_hz < 76800000)
-		cfg->pll_ipdiv = 4;
-	else
-		cfg->pll_ipdiv = 8;
-
-	/*
-	 * Make sure DSI htotal is aligned on a lane boundary when calculating
-	 * the expected data rate. This is done by extending HFP in case of
-	 * misalignment.
-	 */
-	adj_dsi_htotal = dsi_htotal;
-	if (dsi_htotal % dsi_nlanes)
-		adj_dsi_htotal += dsi_nlanes - (dsi_htotal % dsi_nlanes);
-
-	dlane_bps = (u64)dpi_hz * adj_dsi_htotal;
-
-	/* data rate in bytes/sec is not an integer, refuse the mode. */
-	if (do_div(dlane_bps, dsi_nlanes * dpi_htotal))
-		return -EINVAL;
-
-	/* data rate was in bytes/sec, convert to bits/sec. */
-	dlane_bps *= 8;
-
-	if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
-		return -EINVAL;
-	else if (dlane_bps >= 1250000000)
-		cfg->pll_opdiv = 1;
-	else if (dlane_bps >= 630000000)
-		cfg->pll_opdiv = 2;
-	else if (dlane_bps >= 320000000)
-		cfg->pll_opdiv = 4;
-	else if (dlane_bps >= 160000000)
-		cfg->pll_opdiv = 8;
-
-	/*
-	 * Allow a deviation of 0.2% on the per-lane data rate to try to
-	 * recover a potential mismatch between DPI and PPI clks.
-	 */
-	dlane_bps_max = dlane_bps + DIV_ROUND_DOWN_ULL(dlane_bps, 500);
-	fbdiv_max = DIV_ROUND_DOWN_ULL(dlane_bps_max * 2 *
-				       cfg->pll_opdiv * cfg->pll_ipdiv,
-				       pll_ref_hz);
-	fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv *
-				 cfg->pll_ipdiv,
-				 pll_ref_hz);
-
-	/*
-	 * Iterate over all acceptable fbdiv and try to find an adjusted DSI
-	 * htotal length providing an exact match.
-	 *
-	 * Note that we could do something even trickier by relying on the fact
-	 * that a new line is not necessarily aligned on a lane boundary, so,
-	 * by making adj_dsi_htotal non aligned on a dsi_lanes we can improve a
-	 * bit the precision. With this, the step would be
-	 *
-	 *	pll_ref_hz / (2 * opdiv * ipdiv * nlanes)
-	 *
-	 * instead of
-	 *
-	 *	pll_ref_hz / (2 * opdiv * ipdiv)
-	 *
-	 * The drawback of this approach is that we would need to make sure the
-	 * number or lines is a multiple of the realignment periodicity which is
-	 * a function of the number of lanes and the original misalignment. For
-	 * example, for NLANES = 4 and HTOTAL % NLANES = 3, it takes 4 lines
-	 * to realign on a lane:
-	 * LINE 0: expected number of bytes, starts emitting first byte of
-	 *	   LINE 1 on LANE 3
-	 * LINE 1: expected number of bytes, starts emitting first 2 bytes of
-	 *	   LINE 2 on LANES 2 and 3
-	 * LINE 2: expected number of bytes, starts emitting first 3 bytes of
-	 *	   of LINE 3 on LANES 1, 2 and 3
-	 * LINE 3: one byte less, now things are realigned on LANE 0 for LINE 4
-	 *
-	 * I figured this extra complexity was not worth the benefit, but if
-	 * someone really has unfixable mismatch, that would be something to
-	 * investigate.
-	 */
-	for (; fbdiv <= fbdiv_max; fbdiv++) {
-		u32 rem;
-
-		adj_dsi_htotal = (u64)fbdiv * pll_ref_hz * dsi_nlanes *
-				 dpi_htotal;
-
-		/*
-		 * Do the division in 2 steps to avoid an overflow on the
-		 * divider.
-		 */
-		rem = do_div(adj_dsi_htotal, dpi_hz);
-		if (rem)
-			continue;
-
-		rem = do_div(adj_dsi_htotal,
-			     cfg->pll_opdiv * cfg->pll_ipdiv * 2 * 8);
-		if (rem)
-			continue;
-
-		cfg->pll_fbdiv = fbdiv;
-		*dsi_hfp_ext = adj_dsi_htotal - dsi_htotal;
-		break;
-	}
-
-	/* No match, let's just reject the display mode. */
-	if (!cfg->pll_fbdiv)
-		return -EINVAL;
-
-	dlane_bps = DIV_ROUND_DOWN_ULL((u64)dpi_hz * adj_dsi_htotal * 8,
-				       dsi_nlanes * dpi_htotal);
-	cfg->lane_bps = dlane_bps;
-
-	return 0;
-}
-
-static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
-{
-	unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
-	unsigned long psm_div;
-
-	if (!psm_clk_hz || psm_clk_hz > 100000000)
-		return -EINVAL;
-
-	psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
-	if (dphy->ops->set_psm_div)
-		dphy->ops->set_psm_div(dphy, psm_div);
-
-	return 0;
-}
-
-static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
-				       enum cdns_dphy_clk_lane_cfg cfg)
-{
-	if (dphy->ops->set_clk_lane_cfg)
-		dphy->ops->set_clk_lane_cfg(dphy, cfg);
-}
-
-static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
-				  const struct cdns_dphy_cfg *cfg)
-{
-	if (dphy->ops->set_pll_cfg)
-		dphy->ops->set_pll_cfg(dphy, cfg);
-}
-
-static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
-{
-	return dphy->ops->get_wakeup_time_ns(dphy);
-}
-
 static unsigned int dpi_to_dsi_timing(unsigned int dpi_timing,
 				      unsigned int dpi_bpp,
 				      unsigned int dsi_pkt_overhead)
@@ -770,46 +535,12 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi,
 	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)
-{
-	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_htotal += dsi_cfg->hact;
-	dsi_htotal += dsi_cfg->hfp + DSI_HFP_FRAME_OVERHEAD;
-
-	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;
-
-	dsi_cfg->hfp += dsi_hfp_ext;
-	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)
+			       struct cdns_dsi_cfg *dsi_cfg)
 {
 	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;
@@ -818,7 +549,12 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
 	if (ret)
 		return ret;
 
-	ret = cdns_dphy_validate(dsi, dsi_cfg, dphy_cfg, mode);
+	phy_mipi_dphy_get_default_config(mode->crtc_clock * 1000,
+					 mipi_dsi_pixel_format_to_bpp(output->dev->format),
+					 nlanes,
+					 &output->phy_opts.mipi_dphy);
+	drm_display_mode_to_videomode(mode, &phy_cfg->timings);
+	ret = phy_validate(dsi->dphy, PHY_MODE_MIPI_DPHY, &output->phy_opts);
 	if (ret)
 		return ret;
 
@@ -831,7 +567,7 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi,
 	 * is empty before we start a receiving a new line on the DPI
 	 * interface.
 	 */
-	if ((u64)dphy_cfg->lane_bps * mode_to_dpi_hfp(mode) * nlanes <
+	if ((u64)phy_cfg->hs_clk_rate * mode_to_dpi_hfp(mode) * nlanes <
 	    (u64)dsi_hss_hsa_hse_hbp * mode->crtc_clock * 1000)
 		return -EINVAL;
 
@@ -860,7 +596,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;
 
@@ -880,7 +615,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);
+	ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg);
 	if (ret)
 		return MODE_BAD;
 
@@ -903,9 +638,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;
 
 	/*
@@ -916,30 +651,9 @@ 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_configure(dsi->dphy, PHY_MODE_MIPI_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);
@@ -949,7 +663,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);
 }
 
@@ -995,7 +709,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;
@@ -1008,9 +722,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));
+	WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg));
 
-	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),
@@ -1046,9 +760,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);
 
@@ -1362,8 +1075,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;
 }
@@ -1372,8 +1083,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);
@@ -1384,121 +1093,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;
@@ -1537,13 +1131,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) {
@@ -1601,9 +1195,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;
 }
 
@@ -1613,7 +1204,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] 40+ messages in thread

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-05  9:16 ` [PATCH 02/10] phy: Add configuration interface Maxime Ripard
@ 2018-09-05 13:39   ` Laurent Pinchart
  2018-09-06 14:48     ` Maxime Ripard
  2018-09-06  9:27   ` Kishon Vijay Abraham I
  1 sibling, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2018-09-05 13:39 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

Hi Maxime,

Thank you for the patch.

On Wednesday, 5 September 2018 12:16:33 EEST Maxime Ripard wrote:
> The phy framework is only allowing to configure the power state of the PHY
> using the init and power_on hooks, and their power_off and exit
> counterparts.
> 
> While it works for most, simple, PHYs supported so far, some more advanced
> PHYs need some configuration depending on runtime parameters. These PHYs
> have been supported by a number of means already, often by using ad-hoc
> drivers in their consumer drivers.
> 
> That doesn't work too well however, when a consumer device needs to deal

s/deal/deal with/

> multiple PHYs, or when multiple consumers need to deal with the same PHY (a
> DSI driver and a CSI driver for example).
> 
> So we'll add a new interface, through two funtions, phy_validate and
> phy_configure. The first one will allow to check that a current
> configuration, for a given mode, is applicable. It will also allow the PHY
> driver to tune the settings given as parameters as it sees fit.
> 
> phy_configure will actually apply that configuration in the phy itself.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>  drivers/phy/phy-core.c  | 62 ++++++++++++++++++++++++++++++++++++++++++-
>  include/linux/phy/phy.h | 42 ++++++++++++++++++++++++++++-
>  2 files changed, 104 insertions(+)
> 
> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> index 35fd38c5a4a1..6eaf655e370f 100644
> --- a/drivers/phy/phy-core.c
> +++ b/drivers/phy/phy-core.c
> @@ -408,6 +408,68 @@ int phy_calibrate(struct phy *phy)
>  EXPORT_SYMBOL_GPL(phy_calibrate);
> 
>  /**
> + * phy_configure() - Changes the phy parameters
> + * @phy: the phy returned by phy_get()
> + * @mode: phy_mode the configuration is applicable to.
> + * @opts: New configuration to apply
> + *
> + * Used to change the PHY parameters. phy_init() must have
> + * been called on the phy.
> + *
> + * Returns: 0 if successful, an negative error code otherwise
> + */
> +int phy_configure(struct phy *phy, enum phy_mode mode,
> +		  union phy_configure_opts *opts)
> +{
> +	int ret;
> +
> +	if (!phy)
> +		return -EINVAL;
> +
> +	if (!phy->ops->configure)
> +		return 0;

Shouldn't you report an error to the caller ? If a caller expects the PHY to 
be configurable, I would assume that silently ignoring the requested 
configuration won't work great.

> +	mutex_lock(&phy->mutex);
> +	ret = phy->ops->configure(phy, mode, opts);
> +	mutex_unlock(&phy->mutex);
> +
> +	return ret;
> +}
> +
> +/**
> + * phy_validate() - Checks the phy parameters
> + * @phy: the phy returned by phy_get()
> + * @mode: phy_mode the configuration is applicable to.
> + * @opts: Configuration to check
> + *
> + * Used to check that the current set of parameters can be handled by
> + * the phy. Implementations are free to tune the parameters passed as
> + * arguments if needed by some implementation detail or
> + * constraints. It will not change any actual configuration of the
> + * PHY, so calling it as many times as deemed fit will have no side
> + * effect.
> + *
> + * Returns: 0 if successful, an negative error code otherwise
> + */
> +int phy_validate(struct phy *phy, enum phy_mode mode,
> +		  union phy_configure_opts *opts)
> +{
> +	int ret;
> +
> +	if (!phy)
> +		return -EINVAL;
> +
> +	if (!phy->ops->validate)
> +		return 0;
> +
> +	mutex_lock(&phy->mutex);
> +	ret = phy->ops->validate(phy, mode, opts);
> +	mutex_unlock(&phy->mutex);
> +
> +	return ret;
> +}
> +
> +/**
>   * _of_phy_get() - lookup and obtain a reference to a phy by phandle
>   * @np: device_node for which to get the phy
>   * @index: the index of the phy
> diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
> index 9cba7fe16c23..3cc315dcfcd0 100644
> --- a/include/linux/phy/phy.h
> +++ b/include/linux/phy/phy.h
> @@ -44,6 +44,12 @@ enum phy_mode {
>  };
> 
>  /**
> + * union phy_configure_opts - Opaque generic phy configuration
> + */
> +union phy_configure_opts {
> +};
> +
> +/**
>   * struct phy_ops - set of function pointers for performing phy operations
>   * @init: operation to be performed for initializing phy
>   * @exit: operation to be performed while exiting
> @@ -60,6 +66,38 @@ struct phy_ops {
>  	int	(*power_on)(struct phy *phy);
>  	int	(*power_off)(struct phy *phy);
>  	int	(*set_mode)(struct phy *phy, enum phy_mode mode);
> +
> +	/**
> +	 * @configure:
> +	 *
> +	 * Optional.
> +	 *
> +	 * Used to change the PHY parameters. phy_init() must have
> +	 * been called on the phy.
> +	 *
> +	 * Returns: 0 if successful, an negative error code otherwise
> +	 */
> +	int	(*configure)(struct phy *phy, enum phy_mode mode,
> +			     union phy_configure_opts *opts);

Is this function allowed to modify opts ? If so, to what extent ? If not, the 
pointer should be made const.

> +	/**
> +	 * @validate:
> +	 *
> +	 * Optional.
> +	 *
> +	 * Used to check that the current set of parameters can be
> +	 * handled by the phy. Implementations are free to tune the
> +	 * parameters passed as arguments if needed by some
> +	 * implementation detail or constraints. It must not change
> +	 * any actual configuration of the PHY, so calling it as many
> +	 * times as deemed fit by the consumer must have no side
> +	 * effect.
> +	 *
> +	 * Returns: 0 if the configuration can be applied, an negative
> +	 * error code otherwise

When should this operation modify the passed parameters, and when should it 
return an error ? I understand that your goal is to implement a negotiation 
mechanism for the PHY parameters, and to be really useful I think we need to 
document it more precisely.

> +	 */
> +	int	(*validate)(struct phy *phy, enum phy_mode mode,
> +			    union phy_configure_opts *opts);
>  	int	(*reset)(struct phy *phy);
>  	int	(*calibrate)(struct phy *phy);
>  	struct module *owner;
> @@ -164,6 +202,10 @@ int phy_exit(struct phy *phy);
>  int phy_power_on(struct phy *phy);
>  int phy_power_off(struct phy *phy);
>  int phy_set_mode(struct phy *phy, enum phy_mode mode);
> +int phy_configure(struct phy *phy, enum phy_mode mode,
> +		  union phy_configure_opts *opts);
> +int phy_validate(struct phy *phy, enum phy_mode mode,
> +		 union phy_configure_opts *opts);
>  static inline enum phy_mode phy_get_mode(struct phy *phy)
>  {
>  	return phy->attrs.mode;

-- 
Regards,

Laurent Pinchart




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

* Re: [PATCH 03/10] phy: Add MIPI D-PHY configuration options
  2018-09-05  9:16 ` [PATCH 03/10] phy: Add MIPI D-PHY configuration options Maxime Ripard
@ 2018-09-05 13:43   ` Laurent Pinchart
  2018-09-07  8:56     ` Maxime Ripard
  0 siblings, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2018-09-05 13:43 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

Hi Maxime,

Thank you for the patch.

On Wednesday, 5 September 2018 12:16:34 EEST Maxime Ripard wrote:
> Now that we have some infrastructure for it, allow the MIPI D-PHY phy's to
> be configured through the generic functions through a custom structure
> added to the generic union.
> 
> The parameters added here are the one defined in the MIPI D-PHY spec, plus

s/one/ones/

> some parameters that were used by a number of PHY drivers currently found
> in the linux kernel.

It would be useful to document which parameters are from the spec and which 
are not.

> The current set of parameters should cover all the potential users.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>  include/linux/phy/phy-mipi-dphy.h | 241 +++++++++++++++++++++++++++++++-
>  include/linux/phy/phy.h           |   6 +-
>  2 files changed, 247 insertions(+)
>  create mode 100644 include/linux/phy/phy-mipi-dphy.h
> 
> diff --git a/include/linux/phy/phy-mipi-dphy.h
> b/include/linux/phy/phy-mipi-dphy.h new file mode 100644
> index 000000000000..792724145290
> --- /dev/null
> +++ b/include/linux/phy/phy-mipi-dphy.h
> @@ -0,0 +1,241 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2018 Cadence Design Systems Inc.
> + */
> +
> +#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
> + *
> + * This structure is used to represent the configuration state of a
> + * MIPI D-PHY phy.

Shouldn't we split the RX and TX parameters in two structures ?

> + */
> +struct phy_configure_opts_mipi_dphy {
> +	/**
> +	 * @clk_miss:
> +	 *
> +	 * Timeout, in nanoseconds, for receiver to detect absence of
> +	 * Clock transitions and disable the Clock Lane HS-RX.
> +	 */
> +	unsigned int		clk_miss;
> +
> +	/**
> +	 * @clk_post:
> +	 *
> +	 * Time, in nanoseconds, that the transmitter continues to
> +	 * send HS clock after the last associated Data Lane has
> +	 * transitioned to LP Mode. Interval is defined as the period
> +	 * from the end of @hs_trail to the beginning of @clk_trail.
> +	 */
> +	unsigned int		clk_post;
> +
> +	/**
> +	 * @clk_pre:
> +	 *
> +	 * Time, in nanoseconds, that the HS clock shall be driven by
> +	 * the transmitter prior to any associated Data Lane beginning
> +	 * the transition from LP to HS mode.
> +	 */
> +	unsigned int		clk_pre;
> +
> +	/**
> +	 * @clk_prepare:
> +	 *
> +	 * Time, in nanoseconds, that the transmitter drives the Clock
> +	 * Lane LP-00 Line state immediately before the HS-0 Line
> +	 * state starting the HS transmission.
> +	 */
> +	unsigned int		clk_prepare;
> +
> +	/**
> +	 * @clk_settle:
> +	 *
> +	 * Time interval, in nanoseconds, during which the HS receiver
> +	 * should ignore any Clock Lane HS transitions, starting from
> +	 * the beginning of @clk_prepare.
> +	 */
> +	unsigned int		clk_settle;
> +
> +	/**
> +	 * @clk_term_en:
> +	 *
> +	 * Time, in nanoseconds, for the Clock Lane receiver to enable
> +	 * the HS line termination.
> +	 */
> +	unsigned int		clk_term_en;
> +
> +	/**
> +	 * @clk_trail:
> +	 *
> +	 * Time, in nanoseconds, that the transmitter drives the HS-0
> +	 * state after the last payload clock bit of a HS transmission
> +	 * burst.
> +	 */
> +	unsigned int		clk_trail;
> +
> +	/**
> +	 * @clk_zero:
> +	 *
> +	 * Time, in nanoseconds, that the transmitter drives the HS-0
> +	 * state prior to starting the Clock.
> +	 */
> +	unsigned int		clk_zero;
> +
> +	/**
> +	 * @d_term_en:
> +	 *
> +	 * Time, in nanoseconds, for the Data Lane receiver to enable
> +	 * the HS line termination.
> +	 */
> +	unsigned int		d_term_en;
> +
> +	/**
> +	 * @eot:
> +	 *
> +	 * Transmitted time interval, in nanoseconds, from the start
> +	 * of @hs_trail or @clk_trail, to the start of the LP- 11
> +	 * state following a HS burst.
> +	 */
> +	unsigned int		eot;
> +
> +	/**
> +	 * @hs_exit:
> +	 *
> +	 * Time, in nanoseconds, that the transmitter drives LP-11
> +	 * following a HS burst.
> +	 */
> +	unsigned int		hs_exit;
> +
> +	/**
> +	 * @hs_prepare:
> +	 *
> +	 * Time, in nanoseconds, that the transmitter drives the Data
> +	 * Lane LP-00 Line state immediately before the HS-0 Line
> +	 * state starting the HS transmission
> +	 */
> +	unsigned int		hs_prepare;
> +
> +	/**
> +	 * @hs_settle:
> +	 *
> +	 * Time interval, in nanoseconds, during which the HS receiver
> +	 * shall ignore any Data Lane HS transitions, starting from
> +	 * the beginning of @hs_prepare.
> +	 */
> +	unsigned int		hs_settle;
> +
> +	/**
> +	 * @hs_skip:
> +	 *
> +	 * Time interval, in nanoseconds, during which the HS-RX
> +	 * should ignore any transitions on the Data Lane, following a
> +	 * HS burst. The end point of the interval is defined as the
> +	 * beginning of the LP-11 state following the HS burst.
> +	 */
> +	unsigned int		hs_skip;
> +
> +	/**
> +	 * @hs_trail:
> +	 *
> +	 * Time, in nanoseconds, that the transmitter drives the
> +	 * flipped differential state after last payload data bit of a
> +	 * HS transmission burst
> +	 */
> +	unsigned int		hs_trail;
> +
> +	/**
> +	 * @hs_zero:
> +	 *
> +	 * Time, in nanoseconds, that the transmitter drives the HS-0
> +	 * state prior to transmitting the Sync sequence.
> +	 */
> +	unsigned int		hs_zero;
> +
> +	/**
> +	 * @lpx:
> +	 *
> +	 * Transmitted length, in nanoseconds, of any Low-Power state
> +	 * period.
> +	 */
> +	unsigned int		lpx;
> +
> +	/**
> +	 * @ta_get:
> +	 *
> +	 * Time, in nanoseconds, that the new transmitter drives the
> +	 * Bridge state (LP-00) after accepting control during a Link
> +	 * Turnaround.
> +	 */
> +	unsigned int		ta_get;
> +
> +	/**
> +	 * @ta_go:
> +	 *
> +	 * Time, in nanoseconds, that the transmitter drives the
> +	 * Bridge state (LP-00) before releasing control during a Link
> +	 * Turnaround.
> +	 */
> +	unsigned int		ta_go;
> +
> +	/**
> +	 * @ta_sure:
> +	 *
> +	 * Time, in nanoseconds, that the new transmitter waits after
> +	 * the LP-10 state before transmitting the Bridge state
> +	 * (LP-00) during a Link Turnaround.
> +	 */
> +	unsigned int		ta_sure;
> +
> +	/**
> +	 * @wakeup:
> +	 *
> +	 * Time, in nanoseconds, that a transmitter drives a Mark-1
> +	 * state prior to a Stop state in order to initiate an exit
> +	 * from ULPS.
> +	 */
> +	unsigned int		wakeup;
> +
> +	/**
> +	 * @hs_clk_rate:
> +	 *
> +	 * Clock rate, in Hertz, of the high-speed clock.
> +	 */
> +	unsigned long		hs_clk_rate;
> +
> +	/**
> +	 * @lp_clk_rate:
> +	 *
> +	 * Clock rate, in Hertz, of the low-power clock.
> +	 */
> +	unsigned long		lp_clk_rate;
> +
> +	/**
> +	 * @lanes:
> +	 *
> +	 * Number of lanes used for the transmissions.
> +	 */
> +	unsigned char		lanes;
> +
> +	/**
> +	 * @modes:
> +	 *
> +	 * transmission operation mode flags
> +	 */
> +	u32			modes;

Where are those flags defined ?

> +	/**
> +	 * @timings:
> +	 *
> +	 * Video timings associated with the transmission.

That's a pretty vague description...

> +	 */
> +	struct videomode	timings;
> +};
> +
> +/* TODO: Add other modes (burst, commands, etc) */
> +#define MIPI_DPHY_MODE_VIDEO_SYNC_PULSE		BIT(0)
> +
> +#endif /* __PHY_MIPI_DPHY_H_ */
> diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
> index 3cc315dcfcd0..2b7a72f98428 100644
> --- a/include/linux/phy/phy.h
> +++ b/include/linux/phy/phy.h
> @@ -20,6 +20,8 @@
>  #include <linux/pm_runtime.h>
>  #include <linux/regulator/consumer.h>
> 
> +#include <linux/phy/phy-mipi-dphy.h>

You can move this within the existing list of #include's.

> +
>  struct phy;
> 
>  enum phy_mode {
> @@ -45,8 +47,12 @@ enum phy_mode {
> 
>  /**
>   * union phy_configure_opts - Opaque generic phy configuration
> + *
> + * @mipi_dphy:	Configuration set applicable for phys supporting
> + *		the MIPI_DPHY phy mode.
>   */
>  union phy_configure_opts {
> +	struct phy_configure_opts_mipi_dphy	mipi_dphy;
>  };
> 
>  /**

-- 
Regards,

Laurent Pinchart




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

* Re: [PATCH 04/10] phy: dphy: Add configuration helpers
  2018-09-05  9:16 ` [PATCH 04/10] phy: dphy: Add configuration helpers Maxime Ripard
@ 2018-09-05 13:46   ` Laurent Pinchart
  2018-09-07 13:37     ` Maxime Ripard
  0 siblings, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2018-09-05 13:46 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

Hi Maxime,

Thank you for the patch.

On Wednesday, 5 September 2018 12:16:35 EEST Maxime Ripard wrote:
> The MIPI D-PHY spec defines default values and boundaries for most of the
> parameters it defines. Introduce helpers to help drivers get meaningful
> values based on their current parameters, and validate the boundaries of
> these parameters if needed.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>  drivers/phy/Kconfig               |   8 ++-
>  drivers/phy/Makefile              |   1 +-
>  drivers/phy/phy-core-mipi-dphy.c  | 160 +++++++++++++++++++++++++++++++-
>  include/linux/phy/phy-mipi-dphy.h |   6 +-
>  4 files changed, 175 insertions(+)
>  create mode 100644 drivers/phy/phy-core-mipi-dphy.c
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 5c8d452e35e2..06bd22bd1f4a 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -15,6 +15,14 @@ config GENERIC_PHY
>  	  phy users can obtain reference to the PHY. All the users of this
>  	  framework should select this config.
> 
> +config GENERIC_PHY_MIPI_DPHY
> +	bool "MIPI D-PHY support"
> +	help
> +	  Generic MIPI D-PHY support.
> +
> +	  Provides a number of helpers a core functions for MIPI D-PHY
> +	  drivers to us.

Do we really need to make this user-selectable ?

>  config PHY_LPC18XX_USB_OTG
>  	tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver"
>  	depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 84e3bd9c5665..71c29d2b9af7 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -4,6 +4,7 @@
>  #
> 
>  obj-$(CONFIG_GENERIC_PHY)		+= phy-core.o
> +obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY)	+= phy-core-mipi-dphy.o
>  obj-$(CONFIG_PHY_LPC18XX_USB_OTG)	+= phy-lpc18xx-usb-otg.o
>  obj-$(CONFIG_PHY_XGENE)			+= phy-xgene.o
>  obj-$(CONFIG_PHY_PISTACHIO_USB)		+= phy-pistachio-usb.o
> diff --git a/drivers/phy/phy-core-mipi-dphy.c
> b/drivers/phy/phy-core-mipi-dphy.c new file mode 100644
> index 000000000000..6c1ddc7734a2
> --- /dev/null
> +++ b/drivers/phy/phy-core-mipi-dphy.c
> @@ -0,0 +1,160 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2013 NVIDIA Corporation
> + * Copyright (C) 2018 Cadence Design Systems Inc.
> + */
> +
> +#include <linux/errno.h>
> +#include <linux/export.h>
> +#include <linux/kernel.h>
> +#include <linux/time64.h>
> +
> +#include <linux/phy/phy.h>
> +#include <linux/phy/phy-mipi-dphy.h>
> +
> +/*
> + * Default D-PHY timings based on MIPI D-PHY specification. Derived from
> the
> + * valid ranges specified in Section 6.9, Table 14, Page 40 of the D-PHY
> + * specification (v1.2) with minor adjustments.

Could you list those adjustments ?

> + */
> +int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
> +				     unsigned int bpp,
> +				     unsigned int lanes,
> +				     struct phy_configure_opts_mipi_dphy *cfg)
> +{
> +	unsigned long hs_clk_rate;
> +	unsigned long ui;
> +
> +	if (!cfg)
> +		return -EINVAL;

Should we really expect cfg to be NULL ?

> +	hs_clk_rate = pixel_clock * bpp / lanes;
> +	ui = DIV_ROUND_UP(NSEC_PER_SEC, hs_clk_rate);
> +
> +	cfg->clk_miss = 0;
> +	cfg->clk_post = 70 + 52 * ui;
> +	cfg->clk_pre = 8;
> +	cfg->clk_prepare = 65;
> +	cfg->clk_settle = 95;
> +	cfg->clk_term_en = 0;
> +	cfg->clk_trail = 80;
> +	cfg->clk_zero = 260;
> +	cfg->d_term_en = 0;
> +	cfg->eot = 0;
> +	cfg->hs_exit = 120;
> +	cfg->hs_prepare = 65 + 5 * ui;
> +	cfg->hs_zero = 145 + 5 * ui;
> +	cfg->hs_settle = 85 + 6 * ui;
> +	cfg->hs_skip = 40;
> +
> +	/*
> +	 * The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40)
> +	 * contains this formula as:
> +	 *
> +	 *     T_HS-TRAIL = max(n * 8 * ui, 60 + n * 4 * ui)
> +	 *
> +	 * where n = 1 for forward-direction HS mode and n = 4 for reverse-
> +	 * direction HS mode. There's only one setting and this function does
> +	 * not parameterize on anything other that ui, so this code will
> +	 * assumes that reverse-direction HS mode is supported and uses n = 4.
> +	 */
> +	cfg->hs_trail = max(4 * 8 * ui, 60 + 4 * 4 * ui);
> +
> +	cfg->init = 100000;
> +	cfg->lpx = 60;
> +	cfg->ta_get = 5 * cfg->lpx;
> +	cfg->ta_go = 4 * cfg->lpx;
> +	cfg->ta_sure = 2 * cfg->lpx;
> +	cfg->wakeup = 1000000;
> +
> +	cfg->hs_clk_rate = hs_clk_rate;
> +	cfg->lanes = lanes;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(phy_mipi_dphy_get_default_config);
> +
> +/*
> + * Validate D-PHY configuration according to MIPI D-PHY specification
> + * (v1.2, Section Section 6.9 "Global Operation Timing Parameters").
> + */
> +int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
> +{
> +	unsigned long ui;
> +
> +	if (!cfg)
> +		return -EINVAL;

Same here.

> +	ui = DIV_ROUND_UP(NSEC_PER_SEC, cfg->hs_clk_rate);
> +
> +	if (cfg->clk_miss > 60)
> +		return -EINVAL;
> +
> +	if (cfg->clk_post < (60 + 52 * ui))
> +		return -EINVAL;
> +
> +	if (cfg->clk_pre < 8)
> +		return -EINVAL;
> +
> +	if (cfg->clk_prepare < 38 || cfg->clk_prepare > 95)
> +		return -EINVAL;
> +
> +	if (cfg->clk_settle < 95 || cfg->clk_settle > 300)
> +		return -EINVAL;
> +
> +	if (cfg->clk_term_en > 38)
> +		return -EINVAL;
> +
> +	if (cfg->clk_trail < 60)
> +		return -EINVAL;
> +
> +	if (cfg->clk_prepare + cfg->clk_zero < 300)
> +		return -EINVAL;
> +
> +	if (cfg->d_term_en > 35 + 4 * ui)
> +		return -EINVAL;
> +
> +	if (cfg->eot > 105 + 12 * ui)
> +		return -EINVAL;
> +
> +	if (cfg->hs_exit < 100)
> +		return -EINVAL;
> +
> +	if (cfg->hs_prepare < 40 + 4 * ui ||
> +	    cfg->hs_prepare > 85 + 6 * ui)
> +		return -EINVAL;
> +
> +	if (cfg->hs_prepare + cfg->hs_zero < 145 + 10 * ui)
> +		return -EINVAL;
> +
> +	if ((cfg->hs_settle < 85 + 6 * ui) ||
> +	    (cfg->hs_settle > 145 + 10 * ui))
> +		return -EINVAL;
> +
> +	if (cfg->hs_skip < 40 || cfg->hs_skip > 55 + 4 * ui)
> +		return -EINVAL;
> +
> +	if (cfg->hs_trail < max(8 * ui, 60 + 4 * ui))
> +		return -EINVAL;
> +
> +	if (cfg->init < 100000)
> +		return -EINVAL;
> +
> +	if (cfg->lpx < 50)
> +		return -EINVAL;
> +
> +	if (cfg->ta_get != 5 * cfg->lpx)
> +		return -EINVAL;
> +
> +	if (cfg->ta_go != 4 * cfg->lpx)
> +		return -EINVAL;
> +
> +	if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > 2 * cfg->lpx)
> +		return -EINVAL;
> +
> +	if (cfg->wakeup < 1000000)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(phy_mipi_dphy_config_validate);
> diff --git a/include/linux/phy/phy-mipi-dphy.h
> b/include/linux/phy/phy-mipi-dphy.h index 792724145290..7656d057198f 100644
> --- a/include/linux/phy/phy-mipi-dphy.h
> +++ b/include/linux/phy/phy-mipi-dphy.h
> @@ -238,4 +238,10 @@ struct phy_configure_opts_mipi_dphy {
>  /* TODO: Add other modes (burst, commands, etc) */
>  #define MIPI_DPHY_MODE_VIDEO_SYNC_PULSE		BIT(0)
> 
> +int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
> +				     unsigned int bpp,
> +				     unsigned int lanes,
> +				     struct phy_configure_opts_mipi_dphy *cfg);
> +int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy
> *cfg);
> +
>  #endif /* __PHY_MIPI_DPHY_H_ */


-- 
Regards,

Laurent Pinchart




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

* Re: [PATCH 01/10] phy: Add MIPI D-PHY mode
  2018-09-05  9:16 ` [PATCH 01/10] phy: Add MIPI D-PHY mode Maxime Ripard
@ 2018-09-05 13:46   ` Laurent Pinchart
  0 siblings, 0 replies; 40+ messages in thread
From: Laurent Pinchart @ 2018-09-05 13:46 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

Hi Maxime,

Thank you for the patch.

On Wednesday, 5 September 2018 12:16:32 EEST Maxime Ripard wrote:
> MIPI D-PHY is a MIPI standard meant mostly for display and cameras in
> embedded systems. Add a mode for it.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> ---
>  include/linux/phy/phy.h | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
> index 9713aebdd348..9cba7fe16c23 100644
> --- a/include/linux/phy/phy.h
> +++ b/include/linux/phy/phy.h
> @@ -40,6 +40,7 @@ enum phy_mode {
>  	PHY_MODE_10GKR,
>  	PHY_MODE_UFS_HS_A,
>  	PHY_MODE_UFS_HS_B,
> +	PHY_MODE_MIPI_DPHY,
>  };
> 
>  /**

-- 
Regards,

Laurent Pinchart




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

* Re: [PATCH 09/10] phy: Add Cadence D-PHY support
  2018-09-05  9:16 ` [PATCH 09/10] phy: Add Cadence D-PHY support Maxime Ripard
@ 2018-09-05 13:48   ` Laurent Pinchart
  2018-09-07 13:38     ` Maxime Ripard
  0 siblings, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2018-09-05 13:48 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

Hi Maxime,

Thank you for the patch.

On Wednesday, 5 September 2018 12:16:40 EEST Maxime Ripard wrote:
> Cadence has designed a D-PHY that can be used by the, currently in tree,
> DSI bridge (DRM), CSI Transceiver and CSI Receiver (v4l2) drivers.
> 
> Only the DSI driver has an ad-hoc driver for that phy at the moment, while
> the v4l2 drivers are completely missing any phy support. In order to make
> that phy support available to all these drivers, without having to
> duplicate that code three times, let's create a generic phy framework
> driver.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>  drivers/phy/Kconfig             |   1 +-
>  drivers/phy/Makefile            |   1 +-
>  drivers/phy/cadence/Kconfig     |  13 +-
>  drivers/phy/cadence/Makefile    |   1 +-
>  drivers/phy/cadence/cdns-dphy.c | 499 +++++++++++++++++++++++++++++++++-

Should the DT bindings be split from Documentation/devicetree/bindings/
display/bridge/cdns,dsi.txt ?

>  5 files changed, 515 insertions(+)
>  create mode 100644 drivers/phy/cadence/Kconfig
>  create mode 100644 drivers/phy/cadence/Makefile
>  create mode 100644 drivers/phy/cadence/cdns-dphy.c

[snip]

-- 
Regards,

Laurent Pinchart




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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-05  9:16 ` [PATCH 02/10] phy: Add configuration interface Maxime Ripard
  2018-09-05 13:39   ` Laurent Pinchart
@ 2018-09-06  9:27   ` Kishon Vijay Abraham I
  2018-09-06 14:56     ` Maxime Ripard
  1 sibling, 1 reply; 40+ messages in thread
From: Kishon Vijay Abraham I @ 2018-09-06  9:27 UTC (permalink / raw)
  To: Maxime Ripard, Boris Brezillon
  Cc: Thomas Petazzoni, Laurent Pinchart, linux-media, Archit Taneja,
	Andrzej Hajda, Chen-Yu Tsai, linux-kernel, dri-devel,
	linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

Hi,

On Wednesday 05 September 2018 02:46 PM, Maxime Ripard wrote:
> The phy framework is only allowing to configure the power state of the PHY
> using the init and power_on hooks, and their power_off and exit
> counterparts.
> 
> While it works for most, simple, PHYs supported so far, some more advanced
> PHYs need some configuration depending on runtime parameters. These PHYs
> have been supported by a number of means already, often by using ad-hoc
> drivers in their consumer drivers.
> 
> That doesn't work too well however, when a consumer device needs to deal
> multiple PHYs, or when multiple consumers need to deal with the same PHY (a
> DSI driver and a CSI driver for example).
> 
> So we'll add a new interface, through two funtions, phy_validate and
> phy_configure. The first one will allow to check that a current
> configuration, for a given mode, is applicable. It will also allow the PHY
> driver to tune the settings given as parameters as it sees fit.
> 
> phy_configure will actually apply that configuration in the phy itself.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> ---
>  drivers/phy/phy-core.c  | 62 ++++++++++++++++++++++++++++++++++++++++++-
>  include/linux/phy/phy.h | 42 ++++++++++++++++++++++++++++-
>  2 files changed, 104 insertions(+)
> 
> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> index 35fd38c5a4a1..6eaf655e370f 100644
> --- a/drivers/phy/phy-core.c
> +++ b/drivers/phy/phy-core.c
> @@ -408,6 +408,68 @@ int phy_calibrate(struct phy *phy)
>  EXPORT_SYMBOL_GPL(phy_calibrate);
>  
>  /**
> + * phy_configure() - Changes the phy parameters
> + * @phy: the phy returned by phy_get()
> + * @mode: phy_mode the configuration is applicable to.

mode should be used if the same PHY can be configured in multiple modes. But
with phy_set_mode() and phy_calibrate() we could achieve the same.
> + * @opts: New configuration to apply

Should these configuration come from the consumer driver? Can't the helper
functions be directly invoked by the PHY driver for the configuration.
> + *
> + * Used to change the PHY parameters. phy_init() must have
> + * been called on the phy.
> + *
> + * Returns: 0 if successful, an negative error code otherwise
> + */
> +int phy_configure(struct phy *phy, enum phy_mode mode,
> +		  union phy_configure_opts *opts)
> +{> +	int ret;
> +
> +	if (!phy)
> +		return -EINVAL;
> +
> +	if (!phy->ops->configure)
> +		return 0;
> +
> +	mutex_lock(&phy->mutex);
> +	ret = phy->ops->configure(phy, mode, opts);
> +	mutex_unlock(&phy->mutex);
> +
> +	return ret;
> +}
> +
> +/**
> + * phy_validate() - Checks the phy parameters
> + * @phy: the phy returned by phy_get()
> + * @mode: phy_mode the configuration is applicable to.
> + * @opts: Configuration to check
> + *
> + * Used to check that the current set of parameters can be handled by
> + * the phy. Implementations are free to tune the parameters passed as
> + * arguments if needed by some implementation detail or
> + * constraints. It will not change any actual configuration of the
> + * PHY, so calling it as many times as deemed fit will have no side
> + * effect.
> + *
> + * Returns: 0 if successful, an negative error code otherwise
> + */
> +int phy_validate(struct phy *phy, enum phy_mode mode,
> +		  union phy_configure_opts *opts)

IIUC the consumer driver will pass configuration options (or PHY parameters)
which will be validated by the PHY driver and in some cases the PHY driver can
modify the configuration options? And these modified configuration options will
again be given to phy_configure?

Looks like it's a round about way of doing the same thing.


> +{
> +	int ret;
> +
> +	if (!phy)
> +		return -EINVAL;
> +
> +	if (!phy->ops->validate)
> +		return 0;
> +
> +	mutex_lock(&phy->mutex);
> +	ret = phy->ops->validate(phy, mode, opts);
> +	mutex_unlock(&phy->mutex);
> +
> +	return ret;
> +}
> +
> +/**
>   * _of_phy_get() - lookup and obtain a reference to a phy by phandle
>   * @np: device_node for which to get the phy
>   * @index: the index of the phy
> diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
> index 9cba7fe16c23..3cc315dcfcd0 100644
> --- a/include/linux/phy/phy.h
> +++ b/include/linux/phy/phy.h
> @@ -44,6 +44,12 @@ enum phy_mode {
>  };
>  
>  /**
> + * union phy_configure_opts - Opaque generic phy configuration
> + */
> +union phy_configure_opts {
> +};
> +
> +/**
>   * struct phy_ops - set of function pointers for performing phy operations
>   * @init: operation to be performed for initializing phy
>   * @exit: operation to be performed while exiting
> @@ -60,6 +66,38 @@ struct phy_ops {
>  	int	(*power_on)(struct phy *phy);
>  	int	(*power_off)(struct phy *phy);
>  	int	(*set_mode)(struct phy *phy, enum phy_mode mode);
> +
> +	/**
> +	 * @configure:
> +	 *
> +	 * Optional.
> +	 *
> +	 * Used to change the PHY parameters. phy_init() must have
> +	 * been called on the phy.
> +	 *
> +	 * Returns: 0 if successful, an negative error code otherwise
> +	 */
> +	int	(*configure)(struct phy *phy, enum phy_mode mode,
> +			     union phy_configure_opts *opts);
> +
> +	/**
> +	 * @validate:
> +	 *
> +	 * Optional.
> +	 *
> +	 * Used to check that the current set of parameters can be
> +	 * handled by the phy. Implementations are free to tune the
> +	 * parameters passed as arguments if needed by some
> +	 * implementation detail or constraints. It must not change
> +	 * any actual configuration of the PHY, so calling it as many
> +	 * times as deemed fit by the consumer must have no side
> +	 * effect.
> +	 *
> +	 * Returns: 0 if the configuration can be applied, an negative
> +	 * error code otherwise
> +	 */
> +	int	(*validate)(struct phy *phy, enum phy_mode mode,
> +			    union phy_configure_opts *opts);
>  	int	(*reset)(struct phy *phy);
>  	int	(*calibrate)(struct phy *phy);
>  	struct module *owner;
> @@ -164,6 +202,10 @@ int phy_exit(struct phy *phy);
>  int phy_power_on(struct phy *phy);
>  int phy_power_off(struct phy *phy);
>  int phy_set_mode(struct phy *phy, enum phy_mode mode);
> +int phy_configure(struct phy *phy, enum phy_mode mode,
> +		  union phy_configure_opts *opts);
> +int phy_validate(struct phy *phy, enum phy_mode mode,
> +		 union phy_configure_opts *opts);

Stub function when CONFIG_GENERIC_PHY is not set is also required.

Thanks
Kishon

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-05 13:39   ` Laurent Pinchart
@ 2018-09-06 14:48     ` Maxime Ripard
  2018-09-06 16:24       ` Andrew Lunn
  2018-09-06 16:51       ` Laurent Pinchart
  0 siblings, 2 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-06 14:48 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

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

On Wed, Sep 05, 2018 at 04:39:46PM +0300, Laurent Pinchart wrote:
> Hi Maxime,
> 
> Thank you for the patch.
> 
> On Wednesday, 5 September 2018 12:16:33 EEST Maxime Ripard wrote:
> > The phy framework is only allowing to configure the power state of the PHY
> > using the init and power_on hooks, and their power_off and exit
> > counterparts.
> > 
> > While it works for most, simple, PHYs supported so far, some more advanced
> > PHYs need some configuration depending on runtime parameters. These PHYs
> > have been supported by a number of means already, often by using ad-hoc
> > drivers in their consumer drivers.
> > 
> > That doesn't work too well however, when a consumer device needs to deal
> 
> s/deal/deal with/
> 
> > multiple PHYs, or when multiple consumers need to deal with the same PHY (a
> > DSI driver and a CSI driver for example).
> > 
> > So we'll add a new interface, through two funtions, phy_validate and
> > phy_configure. The first one will allow to check that a current
> > configuration, for a given mode, is applicable. It will also allow the PHY
> > driver to tune the settings given as parameters as it sees fit.
> > 
> > phy_configure will actually apply that configuration in the phy itself.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> > ---
> >  drivers/phy/phy-core.c  | 62 ++++++++++++++++++++++++++++++++++++++++++-
> >  include/linux/phy/phy.h | 42 ++++++++++++++++++++++++++++-
> >  2 files changed, 104 insertions(+)
> > 
> > diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> > index 35fd38c5a4a1..6eaf655e370f 100644
> > --- a/drivers/phy/phy-core.c
> > +++ b/drivers/phy/phy-core.c
> > @@ -408,6 +408,68 @@ int phy_calibrate(struct phy *phy)
> >  EXPORT_SYMBOL_GPL(phy_calibrate);
> > 
> >  /**
> > + * phy_configure() - Changes the phy parameters
> > + * @phy: the phy returned by phy_get()
> > + * @mode: phy_mode the configuration is applicable to.
> > + * @opts: New configuration to apply
> > + *
> > + * Used to change the PHY parameters. phy_init() must have
> > + * been called on the phy.
> > + *
> > + * Returns: 0 if successful, an negative error code otherwise
> > + */
> > +int phy_configure(struct phy *phy, enum phy_mode mode,
> > +		  union phy_configure_opts *opts)
> > +{
> > +	int ret;
> > +
> > +	if (!phy)
> > +		return -EINVAL;
> > +
> > +	if (!phy->ops->configure)
> > +		return 0;
> 
> Shouldn't you report an error to the caller ? If a caller expects the PHY to 
> be configurable, I would assume that silently ignoring the requested 
> configuration won't work great.

I'm not sure. I also expect a device having to interact with multiple
PHYs, some of them needing some configuration while some other do
not. In that scenario, returning 0 seems to be the right thing to do.

> > +	mutex_lock(&phy->mutex);
> > +	ret = phy->ops->configure(phy, mode, opts);
> > +	mutex_unlock(&phy->mutex);
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * phy_validate() - Checks the phy parameters
> > + * @phy: the phy returned by phy_get()
> > + * @mode: phy_mode the configuration is applicable to.
> > + * @opts: Configuration to check
> > + *
> > + * Used to check that the current set of parameters can be handled by
> > + * the phy. Implementations are free to tune the parameters passed as
> > + * arguments if needed by some implementation detail or
> > + * constraints. It will not change any actual configuration of the
> > + * PHY, so calling it as many times as deemed fit will have no side
> > + * effect.
> > + *
> > + * Returns: 0 if successful, an negative error code otherwise
> > + */
> > +int phy_validate(struct phy *phy, enum phy_mode mode,
> > +		  union phy_configure_opts *opts)
> > +{
> > +	int ret;
> > +
> > +	if (!phy)
> > +		return -EINVAL;
> > +
> > +	if (!phy->ops->validate)
> > +		return 0;
> > +
> > +	mutex_lock(&phy->mutex);
> > +	ret = phy->ops->validate(phy, mode, opts);
> > +	mutex_unlock(&phy->mutex);
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> >   * _of_phy_get() - lookup and obtain a reference to a phy by phandle
> >   * @np: device_node for which to get the phy
> >   * @index: the index of the phy
> > diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
> > index 9cba7fe16c23..3cc315dcfcd0 100644
> > --- a/include/linux/phy/phy.h
> > +++ b/include/linux/phy/phy.h
> > @@ -44,6 +44,12 @@ enum phy_mode {
> >  };
> > 
> >  /**
> > + * union phy_configure_opts - Opaque generic phy configuration
> > + */
> > +union phy_configure_opts {
> > +};
> > +
> > +/**
> >   * struct phy_ops - set of function pointers for performing phy operations
> >   * @init: operation to be performed for initializing phy
> >   * @exit: operation to be performed while exiting
> > @@ -60,6 +66,38 @@ struct phy_ops {
> >  	int	(*power_on)(struct phy *phy);
> >  	int	(*power_off)(struct phy *phy);
> >  	int	(*set_mode)(struct phy *phy, enum phy_mode mode);
> > +
> > +	/**
> > +	 * @configure:
> > +	 *
> > +	 * Optional.
> > +	 *
> > +	 * Used to change the PHY parameters. phy_init() must have
> > +	 * been called on the phy.
> > +	 *
> > +	 * Returns: 0 if successful, an negative error code otherwise
> > +	 */
> > +	int	(*configure)(struct phy *phy, enum phy_mode mode,
> > +			     union phy_configure_opts *opts);
> 
> Is this function allowed to modify opts ? If so, to what extent ? If not, the 
> pointer should be made const.

That's a pretty good question. I guess it could modify it to the same
extent than validate could. Would that make sense?

> > +	/**
> > +	 * @validate:
> > +	 *
> > +	 * Optional.
> > +	 *
> > +	 * Used to check that the current set of parameters can be
> > +	 * handled by the phy. Implementations are free to tune the
> > +	 * parameters passed as arguments if needed by some
> > +	 * implementation detail or constraints. It must not change
> > +	 * any actual configuration of the PHY, so calling it as many
> > +	 * times as deemed fit by the consumer must have no side
> > +	 * effect.
> > +	 *
> > +	 * Returns: 0 if the configuration can be applied, an negative
> > +	 * error code otherwise
> 
> When should this operation modify the passed parameters, and when should it 
> return an error ? I understand that your goal is to implement a negotiation 
> mechanism for the PHY parameters, and to be really useful I think we need to 
> document it more precisely.

My initial idea was to reject a configuration that wouldn't be
achievable by the PHY, ie you're asking something that is outside of
the operating boundaries, while you would be able to change settings
that would be operational, but sub-optimal.

Maxime

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

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

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-06  9:27   ` Kishon Vijay Abraham I
@ 2018-09-06 14:56     ` Maxime Ripard
  2018-09-12  7:42       ` Kishon Vijay Abraham I
  0 siblings, 1 reply; 40+ messages in thread
From: Maxime Ripard @ 2018-09-06 14:56 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Boris Brezillon, Thomas Petazzoni, Laurent Pinchart, linux-media,
	Archit Taneja, Andrzej Hajda, Chen-Yu Tsai, linux-kernel,
	dri-devel, linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

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

Hi Kishon,

On Thu, Sep 06, 2018 at 02:57:58PM +0530, Kishon Vijay Abraham I wrote:
> On Wednesday 05 September 2018 02:46 PM, Maxime Ripard wrote:
> > The phy framework is only allowing to configure the power state of the PHY
> > using the init and power_on hooks, and their power_off and exit
> > counterparts.
> > 
> > While it works for most, simple, PHYs supported so far, some more advanced
> > PHYs need some configuration depending on runtime parameters. These PHYs
> > have been supported by a number of means already, often by using ad-hoc
> > drivers in their consumer drivers.
> > 
> > That doesn't work too well however, when a consumer device needs to deal
> > multiple PHYs, or when multiple consumers need to deal with the same PHY (a
> > DSI driver and a CSI driver for example).
> > 
> > So we'll add a new interface, through two funtions, phy_validate and
> > phy_configure. The first one will allow to check that a current
> > configuration, for a given mode, is applicable. It will also allow the PHY
> > driver to tune the settings given as parameters as it sees fit.
> > 
> > phy_configure will actually apply that configuration in the phy itself.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> > ---
> >  drivers/phy/phy-core.c  | 62 ++++++++++++++++++++++++++++++++++++++++++-
> >  include/linux/phy/phy.h | 42 ++++++++++++++++++++++++++++-
> >  2 files changed, 104 insertions(+)
> > 
> > diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> > index 35fd38c5a4a1..6eaf655e370f 100644
> > --- a/drivers/phy/phy-core.c
> > +++ b/drivers/phy/phy-core.c
> > @@ -408,6 +408,68 @@ int phy_calibrate(struct phy *phy)
> >  EXPORT_SYMBOL_GPL(phy_calibrate);
> >  
> >  /**
> > + * phy_configure() - Changes the phy parameters
> > + * @phy: the phy returned by phy_get()
> > + * @mode: phy_mode the configuration is applicable to.
> 
> mode should be used if the same PHY can be configured in multiple modes. But
> with phy_set_mode() and phy_calibrate() we could achieve the same.

So you would change the prototype to have a configuration applying
only to the current mode set previously through set_mode?

Can we have PHY that operate in multiple modes at the same time?

> > + * @opts: New configuration to apply
> 
> Should these configuration come from the consumer driver?

Yes

> Can't the helper functions be directly invoked by the PHY driver for
> the configuration.

Not really. The helpers are here to introduce functions that give you
the defaults provided by the spec for a given configuration, and to
validate that a given configuration is within the spec boundaries. I
expect some consumers to need to change the defaults for some more
suited parameters that are still within the boundaries defined by the
spec.

And I'd really want to have that interface being quite generic, and
applicable to other phy modes as well. The allwinner USB PHY for
example require at the moment an extra function that could be moved to
this API:
https://elixir.bootlin.com/linux/latest/source/drivers/phy/allwinner/phy-sun4i-usb.c#L512

> > + *
> > + * Used to change the PHY parameters. phy_init() must have
> > + * been called on the phy.
> > + *
> > + * Returns: 0 if successful, an negative error code otherwise
> > + */
> > +int phy_configure(struct phy *phy, enum phy_mode mode,
> > +		  union phy_configure_opts *opts)
> > +{> +	int ret;
> > +
> > +	if (!phy)
> > +		return -EINVAL;
> > +
> > +	if (!phy->ops->configure)
> > +		return 0;
> > +
> > +	mutex_lock(&phy->mutex);
> > +	ret = phy->ops->configure(phy, mode, opts);
> > +	mutex_unlock(&phy->mutex);
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * phy_validate() - Checks the phy parameters
> > + * @phy: the phy returned by phy_get()
> > + * @mode: phy_mode the configuration is applicable to.
> > + * @opts: Configuration to check
> > + *
> > + * Used to check that the current set of parameters can be handled by
> > + * the phy. Implementations are free to tune the parameters passed as
> > + * arguments if needed by some implementation detail or
> > + * constraints. It will not change any actual configuration of the
> > + * PHY, so calling it as many times as deemed fit will have no side
> > + * effect.
> > + *
> > + * Returns: 0 if successful, an negative error code otherwise
> > + */
> > +int phy_validate(struct phy *phy, enum phy_mode mode,
> > +		  union phy_configure_opts *opts)
> 
> IIUC the consumer driver will pass configuration options (or PHY parameters)
> which will be validated by the PHY driver and in some cases the PHY driver can
> modify the configuration options? And these modified configuration options will
> again be given to phy_configure?
> 
> Looks like it's a round about way of doing the same thing.

Not really. The validate callback allows to check whether a particular
configuration would work, and try to negotiate a set of configurations
that both the consumer and the PHY could work with.

For example, DRM requires this to filter out display modes (ie,
resolutions) that wouldn't be achievable by the PHY so that it's never
exposed to the user, and you don't end up in a situation where the
user select a mode that you knew had zero chance to work.

> > @@ -164,6 +202,10 @@ int phy_exit(struct phy *phy);
> >  int phy_power_on(struct phy *phy);
> >  int phy_power_off(struct phy *phy);
> >  int phy_set_mode(struct phy *phy, enum phy_mode mode);
> > +int phy_configure(struct phy *phy, enum phy_mode mode,
> > +		  union phy_configure_opts *opts);
> > +int phy_validate(struct phy *phy, enum phy_mode mode,
> > +		 union phy_configure_opts *opts);
> 
> Stub function when CONFIG_GENERIC_PHY is not set is also required.

Ok. Thanks!
Maxime

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

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

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-06 14:48     ` Maxime Ripard
@ 2018-09-06 16:24       ` Andrew Lunn
  2018-09-07  9:01         ` Maxime Ripard
  2018-09-06 16:51       ` Laurent Pinchart
  1 sibling, 1 reply; 40+ messages in thread
From: Andrew Lunn @ 2018-09-06 16:24 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Laurent Pinchart, Archit Taneja, Krzysztof Witos, Rafal Ciepiela,
	Boris Brezillon, linux-kernel, dri-devel, Kishon Vijay Abraham I,
	Andrzej Hajda, Chen-Yu Tsai, Thomas Petazzoni, linux-arm-kernel,
	linux-media

> > > +int phy_configure(struct phy *phy, enum phy_mode mode,
> > > +		  union phy_configure_opts *opts)
> > > +{
> > > +	int ret;
> > > +
> > > +	if (!phy)
> > > +		return -EINVAL;
> > > +
> > > +	if (!phy->ops->configure)
> > > +		return 0;
> > 
> > Shouldn't you report an error to the caller ? If a caller expects the PHY to 
> > be configurable, I would assume that silently ignoring the requested 
> > configuration won't work great.
> 
> I'm not sure. I also expect a device having to interact with multiple
> PHYs, some of them needing some configuration while some other do
> not. In that scenario, returning 0 seems to be the right thing to do.

You could return -EOPNOTSUPP. That is common in the network stack. The
caller then has the information to decide if it should keep going, or
return an error.

       Andrew

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-06 14:48     ` Maxime Ripard
  2018-09-06 16:24       ` Andrew Lunn
@ 2018-09-06 16:51       ` Laurent Pinchart
  2018-09-07  9:07         ` Maxime Ripard
  1 sibling, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2018-09-06 16:51 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

Hi Maxime,

On Thursday, 6 September 2018 17:48:07 EEST Maxime Ripard wrote:
> On Wed, Sep 05, 2018 at 04:39:46PM +0300, Laurent Pinchart wrote:
> > On Wednesday, 5 September 2018 12:16:33 EEST Maxime Ripard wrote:
> >> The phy framework is only allowing to configure the power state of the
> >> PHY using the init and power_on hooks, and their power_off and exit
> >> counterparts.
> >> 
> >> While it works for most, simple, PHYs supported so far, some more
> >> advanced PHYs need some configuration depending on runtime parameters.
> >> These PHYs have been supported by a number of means already, often by
> >> using ad-hoc drivers in their consumer drivers.
> >> 
> >> That doesn't work too well however, when a consumer device needs to deal
> > 
> > s/deal/deal with/
> > 
> >> multiple PHYs, or when multiple consumers need to deal with the same PHY
> >> (a DSI driver and a CSI driver for example).
> >> 
> >> So we'll add a new interface, through two funtions, phy_validate and
> >> phy_configure. The first one will allow to check that a current
> >> configuration, for a given mode, is applicable. It will also allow the
> >> PHY driver to tune the settings given as parameters as it sees fit.
> >> 
> >> phy_configure will actually apply that configuration in the phy itself.
> >> 
> >> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> >> ---
> >> 
> >>  drivers/phy/phy-core.c  | 62 +++++++++++++++++++++++++++++++++++++++++-
> >>  include/linux/phy/phy.h | 42 ++++++++++++++++++++++++++++-
> >>  2 files changed, 104 insertions(+)
> >> 
> >> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> >> index 35fd38c5a4a1..6eaf655e370f 100644
> >> --- a/drivers/phy/phy-core.c
> >> +++ b/drivers/phy/phy-core.c
> >> @@ -408,6 +408,68 @@ int phy_calibrate(struct phy *phy)
> >>  EXPORT_SYMBOL_GPL(phy_calibrate);
> >>  
> >>  /**
> >> + * phy_configure() - Changes the phy parameters
> >> + * @phy: the phy returned by phy_get()
> >> + * @mode: phy_mode the configuration is applicable to.
> >> + * @opts: New configuration to apply
> >> + *
> >> + * Used to change the PHY parameters. phy_init() must have
> >> + * been called on the phy.
> >> + *
> >> + * Returns: 0 if successful, an negative error code otherwise
> >> + */
> >> +int phy_configure(struct phy *phy, enum phy_mode mode,
> >> +		  union phy_configure_opts *opts)
> >> +{
> >> +	int ret;
> >> +
> >> +	if (!phy)
> >> +		return -EINVAL;
> >> +
> >> +	if (!phy->ops->configure)
> >> +		return 0;
> > 
> > Shouldn't you report an error to the caller ? If a caller expects the PHY
> > to be configurable, I would assume that silently ignoring the requested
> > configuration won't work great.
> 
> I'm not sure. I also expect a device having to interact with multiple
> PHYs, some of them needing some configuration while some other do
> not. In that scenario, returning 0 seems to be the right thing to do.

It could be up to the caller to decide whether to ignore the error or not when 
the operation isn't implemented. I expect that a call requiring specific 
configuration parameters for a given PHY might want to bail out if the 
configuration can't be applied. On the other hand that should never happen 
when the system is designed correctly, as vendors are not supposed to ship 
kernels that would be broken by design (as in requiring a configure operation 
but not providing it).

> >> +	mutex_lock(&phy->mutex);
> >> +	ret = phy->ops->configure(phy, mode, opts);
> >> +	mutex_unlock(&phy->mutex);
> >> +
> >> +	return ret;
> >> +}

[snip]

> >> diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
> >> index 9cba7fe16c23..3cc315dcfcd0 100644
> >> --- a/include/linux/phy/phy.h
> >> +++ b/include/linux/phy/phy.h

[snip]

> >> @@ -60,6 +66,38 @@ struct phy_ops {
> >>  	int	(*power_on)(struct phy *phy);
> >>  	int	(*power_off)(struct phy *phy);
> >>  	int	(*set_mode)(struct phy *phy, enum phy_mode mode);
> >> +
> >> +	/**
> >> +	 * @configure:
> >> +	 *
> >> +	 * Optional.
> >> +	 *
> >> +	 * Used to change the PHY parameters. phy_init() must have
> >> +	 * been called on the phy.
> >> +	 *
> >> +	 * Returns: 0 if successful, an negative error code otherwise
> >> +	 */
> >> +	int	(*configure)(struct phy *phy, enum phy_mode mode,
> >> +			     union phy_configure_opts *opts);
> > 
> > Is this function allowed to modify opts ? If so, to what extent ? If not,
> > the pointer should be made const.
> 
> That's a pretty good question. I guess it could modify it to the same
> extent than validate could. Would that make sense?

It would, or we could say that PHY users are required to call the validate 
function first, and the the configure function will return an error if the 
passed configuration isn't valid. That would avoid double-validation when the 
PHY user uses .validate().

> >> +	/**
> >> +	 * @validate:
> >> +	 *
> >> +	 * Optional.
> >> +	 *
> >> +	 * Used to check that the current set of parameters can be
> >> +	 * handled by the phy. Implementations are free to tune the
> >> +	 * parameters passed as arguments if needed by some
> >> +	 * implementation detail or constraints. It must not change
> >> +	 * any actual configuration of the PHY, so calling it as many
> >> +	 * times as deemed fit by the consumer must have no side
> >> +	 * effect.
> >> +	 *
> >> +	 * Returns: 0 if the configuration can be applied, an negative
> >> +	 * error code otherwise
> > 
> > When should this operation modify the passed parameters, and when should
> > it return an error ? I understand that your goal is to implement a
> > negotiation mechanism for the PHY parameters, and to be really useful I
> > think we need to document it more precisely.
> 
> My initial idea was to reject a configuration that wouldn't be
> achievable by the PHY, ie you're asking something that is outside of
> the operating boundaries, while you would be able to change settings
> that would be operational, but sub-optimal.

I'm fine with that, let's document it explicitly.

-- 
Regards,

Laurent Pinchart




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

* Re: [PATCH 03/10] phy: Add MIPI D-PHY configuration options
  2018-09-05 13:43   ` Laurent Pinchart
@ 2018-09-07  8:56     ` Maxime Ripard
  2018-09-07 14:50       ` Laurent Pinchart
  0 siblings, 1 reply; 40+ messages in thread
From: Maxime Ripard @ 2018-09-07  8:56 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

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

Hi,

On Wed, Sep 05, 2018 at 04:43:57PM +0300, Laurent Pinchart wrote:
> > The current set of parameters should cover all the potential users.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> > ---
> >  include/linux/phy/phy-mipi-dphy.h | 241 +++++++++++++++++++++++++++++++-
> >  include/linux/phy/phy.h           |   6 +-
> >  2 files changed, 247 insertions(+)
> >  create mode 100644 include/linux/phy/phy-mipi-dphy.h
> > 
> > diff --git a/include/linux/phy/phy-mipi-dphy.h
> > b/include/linux/phy/phy-mipi-dphy.h new file mode 100644
> > index 000000000000..792724145290
> > --- /dev/null
> > +++ b/include/linux/phy/phy-mipi-dphy.h
> > @@ -0,0 +1,241 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2018 Cadence Design Systems Inc.
> > + */
> > +
> > +#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
> > + *
> > + * This structure is used to represent the configuration state of a
> > + * MIPI D-PHY phy.
> 
> Shouldn't we split the RX and TX parameters in two structures ?

Are they different? As far as I understood it, both were having the
same parameters.



> > +	/**
> > +	 * @modes:
> > +	 *
> > +	 * transmission operation mode flags
> > +	 */
> > +	u32			modes;
> 
> Where are those flags defined ?

goto label;

> > +	/**
> > +	 * @timings:
> > +	 *
> > +	 * Video timings associated with the transmission.
> 
> That's a pretty vague description...

I'll try to improve it then

> > +	 */
> > +	struct videomode	timings;
> > +};
> > +

label:
> > +/* TODO: Add other modes (burst, commands, etc) */
> > +#define MIPI_DPHY_MODE_VIDEO_SYNC_PULSE		BIT(0)

But maybe I should reorganize it to make it more obvious.

Maxime

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

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

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-06 16:24       ` Andrew Lunn
@ 2018-09-07  9:01         ` Maxime Ripard
  0 siblings, 0 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-07  9:01 UTC (permalink / raw)
  To: Andrew Lunn
  Cc: Laurent Pinchart, Archit Taneja, Krzysztof Witos, Rafal Ciepiela,
	Boris Brezillon, linux-kernel, dri-devel, Kishon Vijay Abraham I,
	Andrzej Hajda, Chen-Yu Tsai, Thomas Petazzoni, linux-arm-kernel,
	linux-media

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

On Thu, Sep 06, 2018 at 06:24:50PM +0200, Andrew Lunn wrote:
> > > > +int phy_configure(struct phy *phy, enum phy_mode mode,
> > > > +		  union phy_configure_opts *opts)
> > > > +{
> > > > +	int ret;
> > > > +
> > > > +	if (!phy)
> > > > +		return -EINVAL;
> > > > +
> > > > +	if (!phy->ops->configure)
> > > > +		return 0;
> > > 
> > > Shouldn't you report an error to the caller ? If a caller expects the PHY to 
> > > be configurable, I would assume that silently ignoring the requested 
> > > configuration won't work great.
> > 
> > I'm not sure. I also expect a device having to interact with multiple
> > PHYs, some of them needing some configuration while some other do
> > not. In that scenario, returning 0 seems to be the right thing to do.
> 
> You could return -EOPNOTSUPP. That is common in the network stack. The
> caller then has the information to decide if it should keep going, or
> return an error.

Ok, that works for me then.

Maxime

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

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

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-06 16:51       ` Laurent Pinchart
@ 2018-09-07  9:07         ` Maxime Ripard
  0 siblings, 0 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-07  9:07 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

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

On Thu, Sep 06, 2018 at 07:51:05PM +0300, Laurent Pinchart wrote:
> On Thursday, 6 September 2018 17:48:07 EEST Maxime Ripard wrote:
> > On Wed, Sep 05, 2018 at 04:39:46PM +0300, Laurent Pinchart wrote:
> > > On Wednesday, 5 September 2018 12:16:33 EEST Maxime Ripard wrote:
> > >> The phy framework is only allowing to configure the power state of the
> > >> PHY using the init and power_on hooks, and their power_off and exit
> > >> counterparts.
> > >> 
> > >> While it works for most, simple, PHYs supported so far, some more
> > >> advanced PHYs need some configuration depending on runtime parameters.
> > >> These PHYs have been supported by a number of means already, often by
> > >> using ad-hoc drivers in their consumer drivers.
> > >> 
> > >> That doesn't work too well however, when a consumer device needs to deal
> > > 
> > > s/deal/deal with/
> > > 
> > >> multiple PHYs, or when multiple consumers need to deal with the same PHY
> > >> (a DSI driver and a CSI driver for example).
> > >> 
> > >> So we'll add a new interface, through two funtions, phy_validate and
> > >> phy_configure. The first one will allow to check that a current
> > >> configuration, for a given mode, is applicable. It will also allow the
> > >> PHY driver to tune the settings given as parameters as it sees fit.
> > >> 
> > >> phy_configure will actually apply that configuration in the phy itself.
> > >> 
> > >> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> > >> ---
> > >> 
> > >>  drivers/phy/phy-core.c  | 62 +++++++++++++++++++++++++++++++++++++++++-
> > >>  include/linux/phy/phy.h | 42 ++++++++++++++++++++++++++++-
> > >>  2 files changed, 104 insertions(+)
> > >> 
> > >> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> > >> index 35fd38c5a4a1..6eaf655e370f 100644
> > >> --- a/drivers/phy/phy-core.c
> > >> +++ b/drivers/phy/phy-core.c
> > >> @@ -408,6 +408,68 @@ int phy_calibrate(struct phy *phy)
> > >>  EXPORT_SYMBOL_GPL(phy_calibrate);
> > >>  
> > >>  /**
> > >> + * phy_configure() - Changes the phy parameters
> > >> + * @phy: the phy returned by phy_get()
> > >> + * @mode: phy_mode the configuration is applicable to.
> > >> + * @opts: New configuration to apply
> > >> + *
> > >> + * Used to change the PHY parameters. phy_init() must have
> > >> + * been called on the phy.
> > >> + *
> > >> + * Returns: 0 if successful, an negative error code otherwise
> > >> + */
> > >> +int phy_configure(struct phy *phy, enum phy_mode mode,
> > >> +		  union phy_configure_opts *opts)
> > >> +{
> > >> +	int ret;
> > >> +
> > >> +	if (!phy)
> > >> +		return -EINVAL;
> > >> +
> > >> +	if (!phy->ops->configure)
> > >> +		return 0;
> > > 
> > > Shouldn't you report an error to the caller ? If a caller expects the PHY
> > > to be configurable, I would assume that silently ignoring the requested
> > > configuration won't work great.
> > 
> > I'm not sure. I also expect a device having to interact with multiple
> > PHYs, some of them needing some configuration while some other do
> > not. In that scenario, returning 0 seems to be the right thing to do.
> 
> It could be up to the caller to decide whether to ignore the error or not when 
> the operation isn't implemented. I expect that a call requiring specific 
> configuration parameters for a given PHY might want to bail out if the 
> configuration can't be applied. On the other hand that should never happen 
> when the system is designed correctly, as vendors are not supposed to ship 
> kernels that would be broken by design (as in requiring a configure operation 
> but not providing it).

I'll do as Andrew (and you) suggested then.

> > >> @@ -60,6 +66,38 @@ struct phy_ops {
> > >>  	int	(*power_on)(struct phy *phy);
> > >>  	int	(*power_off)(struct phy *phy);
> > >>  	int	(*set_mode)(struct phy *phy, enum phy_mode mode);
> > >> +
> > >> +	/**
> > >> +	 * @configure:
> > >> +	 *
> > >> +	 * Optional.
> > >> +	 *
> > >> +	 * Used to change the PHY parameters. phy_init() must have
> > >> +	 * been called on the phy.
> > >> +	 *
> > >> +	 * Returns: 0 if successful, an negative error code otherwise
> > >> +	 */
> > >> +	int	(*configure)(struct phy *phy, enum phy_mode mode,
> > >> +			     union phy_configure_opts *opts);
> > > 
> > > Is this function allowed to modify opts ? If so, to what extent ? If not,
> > > the pointer should be made const.
> > 
> > That's a pretty good question. I guess it could modify it to the same
> > extent than validate could. Would that make sense?
> 
> It would, or we could say that PHY users are required to call the validate 
> function first, and the the configure function will return an error if the 
> passed configuration isn't valid. That would avoid double-validation when the 
> PHY user uses .validate().

I usually prefer to have a function being able to check its input on
its own. Especially, the sole use case we have right now is DRM, and
DRM would typically call phy_validate X+1 times (X being the number of
modes), once for each mode in mode_valid and once in atomic_check.

> > >> +	/**
> > >> +	 * @validate:
> > >> +	 *
> > >> +	 * Optional.
> > >> +	 *
> > >> +	 * Used to check that the current set of parameters can be
> > >> +	 * handled by the phy. Implementations are free to tune the
> > >> +	 * parameters passed as arguments if needed by some
> > >> +	 * implementation detail or constraints. It must not change
> > >> +	 * any actual configuration of the PHY, so calling it as many
> > >> +	 * times as deemed fit by the consumer must have no side
> > >> +	 * effect.
> > >> +	 *
> > >> +	 * Returns: 0 if the configuration can be applied, an negative
> > >> +	 * error code otherwise
> > > 
> > > When should this operation modify the passed parameters, and when should
> > > it return an error ? I understand that your goal is to implement a
> > > negotiation mechanism for the PHY parameters, and to be really useful I
> > > think we need to document it more precisely.
> > 
> > My initial idea was to reject a configuration that wouldn't be
> > achievable by the PHY, ie you're asking something that is outside of
> > the operating boundaries, while you would be able to change settings
> > that would be operational, but sub-optimal.
> 
> I'm fine with that, let's document it explicitly.

ACK.

Maxime

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

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

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

* Re: [PATCH 04/10] phy: dphy: Add configuration helpers
  2018-09-05 13:46   ` Laurent Pinchart
@ 2018-09-07 13:37     ` Maxime Ripard
  2018-09-07 14:26       ` Laurent Pinchart
  0 siblings, 1 reply; 40+ messages in thread
From: Maxime Ripard @ 2018-09-07 13:37 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

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

On Wed, Sep 05, 2018 at 04:46:05PM +0300, Laurent Pinchart wrote:
> Hi Maxime,
> 
> Thank you for the patch.
> 
> On Wednesday, 5 September 2018 12:16:35 EEST Maxime Ripard wrote:
> > The MIPI D-PHY spec defines default values and boundaries for most of the
> > parameters it defines. Introduce helpers to help drivers get meaningful
> > values based on their current parameters, and validate the boundaries of
> > these parameters if needed.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> > ---
> >  drivers/phy/Kconfig               |   8 ++-
> >  drivers/phy/Makefile              |   1 +-
> >  drivers/phy/phy-core-mipi-dphy.c  | 160 +++++++++++++++++++++++++++++++-
> >  include/linux/phy/phy-mipi-dphy.h |   6 +-
> >  4 files changed, 175 insertions(+)
> >  create mode 100644 drivers/phy/phy-core-mipi-dphy.c
> > 
> > diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> > index 5c8d452e35e2..06bd22bd1f4a 100644
> > --- a/drivers/phy/Kconfig
> > +++ b/drivers/phy/Kconfig
> > @@ -15,6 +15,14 @@ config GENERIC_PHY
> >  	  phy users can obtain reference to the PHY. All the users of this
> >  	  framework should select this config.
> > 
> > +config GENERIC_PHY_MIPI_DPHY
> > +	bool "MIPI D-PHY support"
> > +	help
> > +	  Generic MIPI D-PHY support.
> > +
> > +	  Provides a number of helpers a core functions for MIPI D-PHY
> > +	  drivers to us.
> 
> Do we really need to make this user-selectable ?

Probably not :)

> >  config PHY_LPC18XX_USB_OTG
> >  	tristate "NXP LPC18xx/43xx SoC USB OTG PHY driver"
> >  	depends on OF && (ARCH_LPC18XX || COMPILE_TEST)
> > diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> > index 84e3bd9c5665..71c29d2b9af7 100644
> > --- a/drivers/phy/Makefile
> > +++ b/drivers/phy/Makefile
> > @@ -4,6 +4,7 @@
> >  #
> > 
> >  obj-$(CONFIG_GENERIC_PHY)		+= phy-core.o
> > +obj-$(CONFIG_GENERIC_PHY_MIPI_DPHY)	+= phy-core-mipi-dphy.o
> >  obj-$(CONFIG_PHY_LPC18XX_USB_OTG)	+= phy-lpc18xx-usb-otg.o
> >  obj-$(CONFIG_PHY_XGENE)			+= phy-xgene.o
> >  obj-$(CONFIG_PHY_PISTACHIO_USB)		+= phy-pistachio-usb.o
> > diff --git a/drivers/phy/phy-core-mipi-dphy.c
> > b/drivers/phy/phy-core-mipi-dphy.c new file mode 100644
> > index 000000000000..6c1ddc7734a2
> > --- /dev/null
> > +++ b/drivers/phy/phy-core-mipi-dphy.c
> > @@ -0,0 +1,160 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2013 NVIDIA Corporation
> > + * Copyright (C) 2018 Cadence Design Systems Inc.
> > + */
> > +
> > +#include <linux/errno.h>
> > +#include <linux/export.h>
> > +#include <linux/kernel.h>
> > +#include <linux/time64.h>
> > +
> > +#include <linux/phy/phy.h>
> > +#include <linux/phy/phy-mipi-dphy.h>
> > +
> > +/*
> > + * Default D-PHY timings based on MIPI D-PHY specification. Derived from
> > the
> > + * valid ranges specified in Section 6.9, Table 14, Page 40 of the D-PHY
> > + * specification (v1.2) with minor adjustments.
> 
> Could you list those adjustments ?

I will. This was taken from the Tegra DSI driver, so I'm not sure what
these are exactly, but that should be addressed.

> > + */
> > +int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
> > +				     unsigned int bpp,
> > +				     unsigned int lanes,
> > +				     struct phy_configure_opts_mipi_dphy *cfg)
> > +{
> > +	unsigned long hs_clk_rate;
> > +	unsigned long ui;
> > +
> > +	if (!cfg)
> > +		return -EINVAL;
> 
> Should we really expect cfg to be NULL ?

It avoids a kernel panic and it's not in a hot patch, so I'd say yes?

Maxime

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

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

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

* Re: [PATCH 09/10] phy: Add Cadence D-PHY support
  2018-09-05 13:48   ` Laurent Pinchart
@ 2018-09-07 13:38     ` Maxime Ripard
  0 siblings, 0 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-07 13:38 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

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

On Wed, Sep 05, 2018 at 04:48:27PM +0300, Laurent Pinchart wrote:
> Hi Maxime,
> 
> Thank you for the patch.
> 
> On Wednesday, 5 September 2018 12:16:40 EEST Maxime Ripard wrote:
> > Cadence has designed a D-PHY that can be used by the, currently in tree,
> > DSI bridge (DRM), CSI Transceiver and CSI Receiver (v4l2) drivers.
> > 
> > Only the DSI driver has an ad-hoc driver for that phy at the moment, while
> > the v4l2 drivers are completely missing any phy support. In order to make
> > that phy support available to all these drivers, without having to
> > duplicate that code three times, let's create a generic phy framework
> > driver.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> > ---
> >  drivers/phy/Kconfig             |   1 +-
> >  drivers/phy/Makefile            |   1 +-
> >  drivers/phy/cadence/Kconfig     |  13 +-
> >  drivers/phy/cadence/Makefile    |   1 +-
> >  drivers/phy/cadence/cdns-dphy.c | 499 +++++++++++++++++++++++++++++++++-
> 
> Should the DT bindings be split from Documentation/devicetree/bindings/
> display/bridge/cdns,dsi.txt ?

Yep, I'll change it.

Thanks!
Maxime

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

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

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

* Re: [PATCH 04/10] phy: dphy: Add configuration helpers
  2018-09-07 13:37     ` Maxime Ripard
@ 2018-09-07 14:26       ` Laurent Pinchart
  2018-09-10 14:16         ` Maxime Ripard
  0 siblings, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2018-09-07 14:26 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

Hi Maxime,

On Friday, 7 September 2018 16:37:39 EEST Maxime Ripard wrote:
> On Wed, Sep 05, 2018 at 04:46:05PM +0300, Laurent Pinchart wrote:
> > On Wednesday, 5 September 2018 12:16:35 EEST Maxime Ripard wrote:
> >> The MIPI D-PHY spec defines default values and boundaries for most of
> >> the parameters it defines. Introduce helpers to help drivers get
> >> meaningful values based on their current parameters, and validate the
> >> boundaries of these parameters if needed.
> >> 
> >> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> >> ---
> >> 
> >>  drivers/phy/Kconfig               |   8 ++-
> >>  drivers/phy/Makefile              |   1 +-
> >>  drivers/phy/phy-core-mipi-dphy.c  | 160 ++++++++++++++++++++++++++++++-
> >>  include/linux/phy/phy-mipi-dphy.h |   6 +-
> >>  4 files changed, 175 insertions(+)
> >>  create mode 100644 drivers/phy/phy-core-mipi-dphy.c

[snip]

> >> diff --git a/drivers/phy/phy-core-mipi-dphy.c
> >> b/drivers/phy/phy-core-mipi-dphy.c new file mode 100644
> >> index 000000000000..6c1ddc7734a2
> >> --- /dev/null
> >> +++ b/drivers/phy/phy-core-mipi-dphy.c
> >> @@ -0,0 +1,160 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2013 NVIDIA Corporation
> >> + * Copyright (C) 2018 Cadence Design Systems Inc.
> >> + */
> >> +
> >> +#include <linux/errno.h>
> >> +#include <linux/export.h>
> >> +#include <linux/kernel.h>
> >> +#include <linux/time64.h>
> >> +
> >> +#include <linux/phy/phy.h>
> >> +#include <linux/phy/phy-mipi-dphy.h>
> >> +
> >> +/*
> >> + * Default D-PHY timings based on MIPI D-PHY specification. Derived
> >> from the
> >> + * valid ranges specified in Section 6.9, Table 14, Page 40 of the
> >> D-PHY
> >> + * specification (v1.2) with minor adjustments.
> > 
> > Could you list those adjustments ?
> 
> I will. This was taken from the Tegra DSI driver, so I'm not sure what
> these are exactly, but that should be addressed.
> 
> >> + */
> >> +int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
> >> +				     unsigned int bpp,
> >> +				     unsigned int lanes,
> >> +				     struct phy_configure_opts_mipi_dphy *cfg)
> >> +{
> >> +	unsigned long hs_clk_rate;
> >> +	unsigned long ui;
> >> +
> >> +	if (!cfg)
> >> +		return -EINVAL;
> > 
> > Should we really expect cfg to be NULL ?
> 
> It avoids a kernel panic and it's not in a hot patch, so I'd say yes?

A few line below you divide by the lanes parameter without checking whether it 
is equal to 0 first, which would also cause issues.

I believe that invalid values in input parameters should only be handled 
explicitly when considered acceptable for the caller to pass such values. In 
this case a NULL cfg pointer is a bug in the caller, which would get noticed 
during development if the kernel panics.

-- 
Regards,

Laurent Pinchart




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

* Re: [PATCH 03/10] phy: Add MIPI D-PHY configuration options
  2018-09-07  8:56     ` Maxime Ripard
@ 2018-09-07 14:50       ` Laurent Pinchart
  2018-09-10 14:18         ` Maxime Ripard
  0 siblings, 1 reply; 40+ messages in thread
From: Laurent Pinchart @ 2018-09-07 14:50 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

Hi Maxime,

On Friday, 7 September 2018 11:56:23 EEST Maxime Ripard wrote:
> On Wed, Sep 05, 2018 at 04:43:57PM +0300, Laurent Pinchart wrote:
> >> The current set of parameters should cover all the potential users.
> >> 
> >> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> >> ---
> >> 
> >>  include/linux/phy/phy-mipi-dphy.h | 241 ++++++++++++++++++++++++++++++-
> >>  include/linux/phy/phy.h           |   6 +-
> >>  2 files changed, 247 insertions(+)
> >>  create mode 100644 include/linux/phy/phy-mipi-dphy.h
> >> 
> >> diff --git a/include/linux/phy/phy-mipi-dphy.h
> >> b/include/linux/phy/phy-mipi-dphy.h new file mode 100644
> >> index 000000000000..792724145290
> >> --- /dev/null
> >> +++ b/include/linux/phy/phy-mipi-dphy.h
> >> @@ -0,0 +1,241 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2018 Cadence Design Systems Inc.
> >> + */
> >> +
> >> +#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
> >> + *
> >> + * This structure is used to represent the configuration state of a
> >> + * MIPI D-PHY phy.
> > 
> > Shouldn't we split the RX and TX parameters in two structures ?
> 
> Are they different? As far as I understood it, both were having the
> same parameters.

clk_miss, for instance, is a receiver parameter, while clk_post is a 
transmitter parameter. There are relationships between the transmitter and 
receiver parameters in the sense that they have to be compatible, and we may 
want to compute one set of parameters based on the other one, but I think they 
target RX and TX separately.

> >> +	/**
> >> +	 * @modes:
> >> +	 *
> >> +	 * transmission operation mode flags
> >> +	 */
> >> +	u32			modes;
> > 
> > Where are those flags defined ?
> 
> goto label;
> 
> >> +	/**
> >> +	 * @timings:
> >> +	 *
> >> +	 * Video timings associated with the transmission.
> > 
> > That's a pretty vague description...
> 
> I'll try to improve it then
> 
> >> +	 */
> >> +	struct videomode	timings;
> >> +};
> >> +
> 
> label:
> > > +/* TODO: Add other modes (burst, commands, etc) */
> > > +#define MIPI_DPHY_MODE_VIDEO_SYNC_PULSE		BIT(0)
> 
> But maybe I should reorganize it to make it more obvious.

-- 
Regards,

Laurent Pinchart




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

* Re: [PATCH 04/10] phy: dphy: Add configuration helpers
  2018-09-07 14:26       ` Laurent Pinchart
@ 2018-09-10 14:16         ` Maxime Ripard
  2018-09-10 14:28           ` Laurent Pinchart
  0 siblings, 1 reply; 40+ messages in thread
From: Maxime Ripard @ 2018-09-10 14:16 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

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

Hi,

On Fri, Sep 07, 2018 at 05:26:29PM +0300, Laurent Pinchart wrote:
> > >> + */
> > >> +int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
> > >> +				     unsigned int bpp,
> > >> +				     unsigned int lanes,
> > >> +				     struct phy_configure_opts_mipi_dphy *cfg)
> > >> +{
> > >> +	unsigned long hs_clk_rate;
> > >> +	unsigned long ui;
> > >> +
> > >> +	if (!cfg)
> > >> +		return -EINVAL;
> > > 
> > > Should we really expect cfg to be NULL ?
> > 
> > It avoids a kernel panic and it's not in a hot patch, so I'd say yes?
> 
> A few line below you divide by the lanes parameter without checking whether it 
> is equal to 0 first, which would also cause issues.

You say that like it would be a bad thing to test for this.

> I believe that invalid values in input parameters should only be handled 
> explicitly when considered acceptable for the caller to pass such values. In 
> this case a NULL cfg pointer is a bug in the caller, which would get noticed 
> during development if the kernel panics.

In the common case, yes. In the case where that pointer is actually
being lost by the caller somewhere down the line and you have to wait
for a while before it happens, then having the driver inoperant
instead of just having a panic seems like the right thing to do.

Maxime

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

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

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

* Re: [PATCH 03/10] phy: Add MIPI D-PHY configuration options
  2018-09-07 14:50       ` Laurent Pinchart
@ 2018-09-10 14:18         ` Maxime Ripard
  0 siblings, 0 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-10 14:18 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

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

On Fri, Sep 07, 2018 at 05:50:52PM +0300, Laurent Pinchart wrote:
> On Friday, 7 September 2018 11:56:23 EEST Maxime Ripard wrote:
> > On Wed, Sep 05, 2018 at 04:43:57PM +0300, Laurent Pinchart wrote:
> > >> The current set of parameters should cover all the potential users.
> > >> 
> > >> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> > >> ---
> > >> 
> > >>  include/linux/phy/phy-mipi-dphy.h | 241 ++++++++++++++++++++++++++++++-
> > >>  include/linux/phy/phy.h           |   6 +-
> > >>  2 files changed, 247 insertions(+)
> > >>  create mode 100644 include/linux/phy/phy-mipi-dphy.h
> > >> 
> > >> diff --git a/include/linux/phy/phy-mipi-dphy.h
> > >> b/include/linux/phy/phy-mipi-dphy.h new file mode 100644
> > >> index 000000000000..792724145290
> > >> --- /dev/null
> > >> +++ b/include/linux/phy/phy-mipi-dphy.h
> > >> @@ -0,0 +1,241 @@
> > >> +/* SPDX-License-Identifier: GPL-2.0 */
> > >> +/*
> > >> + * Copyright (C) 2018 Cadence Design Systems Inc.
> > >> + */
> > >> +
> > >> +#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
> > >> + *
> > >> + * This structure is used to represent the configuration state of a
> > >> + * MIPI D-PHY phy.
> > > 
> > > Shouldn't we split the RX and TX parameters in two structures ?
> > 
> > Are they different? As far as I understood it, both were having the
> > same parameters.
> 
> clk_miss, for instance, is a receiver parameter, while clk_post is a 
> transmitter parameter. There are relationships between the transmitter and 
> receiver parameters in the sense that they have to be compatible, and we may 
> want to compute one set of parameters based on the other one, but I think they 
> target RX and TX separately.

That would require however to have to fill a structure in the consumer
whose sole purpose would be to validate things in the phy
framework. That looks quite weird from an API point-of-view.

Maxime

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

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

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

* Re: [PATCH 04/10] phy: dphy: Add configuration helpers
  2018-09-10 14:16         ` Maxime Ripard
@ 2018-09-10 14:28           ` Laurent Pinchart
  0 siblings, 0 replies; 40+ messages in thread
From: Laurent Pinchart @ 2018-09-10 14:28 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Kishon Vijay Abraham I, Boris Brezillon, Thomas Petazzoni,
	linux-media, Archit Taneja, Andrzej Hajda, Chen-Yu Tsai,
	linux-kernel, dri-devel, linux-arm-kernel, Krzysztof Witos,
	Rafal Ciepiela

Hi Maxime,

On Monday, 10 September 2018 17:16:03 EEST Maxime Ripard wrote:
> On Fri, Sep 07, 2018 at 05:26:29PM +0300, Laurent Pinchart wrote:
> >>>> + */
> >>>> +int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
> >>>> +				     unsigned int bpp,
> >>>> +				     unsigned int lanes,
> >>>> +				     struct phy_configure_opts_mipi_dphy *cfg)
> >>>> +{
> >>>> +	unsigned long hs_clk_rate;
> >>>> +	unsigned long ui;
> >>>> +
> >>>> +	if (!cfg)
> >>>> +		return -EINVAL;
> >>> 
> >>> Should we really expect cfg to be NULL ?
> >> 
> >> It avoids a kernel panic and it's not in a hot patch, so I'd say yes?
> > 
> > A few line below you divide by the lanes parameter without checking
> > whether it is equal to 0 first, which would also cause issues.
> 
> You say that like it would be a bad thing to test for this.
> 
> > I believe that invalid values in input parameters should only be handled
> > explicitly when considered acceptable for the caller to pass such values.
> > In this case a NULL cfg pointer is a bug in the caller, which would get
> > noticed during development if the kernel panics.
> 
> In the common case, yes. In the case where that pointer is actually
> being lost by the caller somewhere down the line and you have to wait
> for a while before it happens, then having the driver inoperant
> instead of just having a panic seems like the right thing to do.

But why would it happen in the first place ? Why would the pointer be more 
likely here to be NULL than to contain, for instance, an uninitialized value, 
which we don't guard against ?

-- 
Regards,

Laurent Pinchart




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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-06 14:56     ` Maxime Ripard
@ 2018-09-12  7:42       ` Kishon Vijay Abraham I
  2018-09-12  8:42         ` Maxime Ripard
  0 siblings, 1 reply; 40+ messages in thread
From: Kishon Vijay Abraham I @ 2018-09-12  7:42 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Boris Brezillon, Thomas Petazzoni, Laurent Pinchart, linux-media,
	Archit Taneja, Andrzej Hajda, Chen-Yu Tsai, linux-kernel,
	dri-devel, linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

Hi,

On Thursday 06 September 2018 08:26 PM, Maxime Ripard wrote:
> Hi Kishon,
> 
> On Thu, Sep 06, 2018 at 02:57:58PM +0530, Kishon Vijay Abraham I wrote:
>> On Wednesday 05 September 2018 02:46 PM, Maxime Ripard wrote:
>>> The phy framework is only allowing to configure the power state of the PHY
>>> using the init and power_on hooks, and their power_off and exit
>>> counterparts.
>>>
>>> While it works for most, simple, PHYs supported so far, some more advanced
>>> PHYs need some configuration depending on runtime parameters. These PHYs
>>> have been supported by a number of means already, often by using ad-hoc
>>> drivers in their consumer drivers.
>>>
>>> That doesn't work too well however, when a consumer device needs to deal
>>> multiple PHYs, or when multiple consumers need to deal with the same PHY (a
>>> DSI driver and a CSI driver for example).
>>>
>>> So we'll add a new interface, through two funtions, phy_validate and
>>> phy_configure. The first one will allow to check that a current
>>> configuration, for a given mode, is applicable. It will also allow the PHY
>>> driver to tune the settings given as parameters as it sees fit.
>>>
>>> phy_configure will actually apply that configuration in the phy itself.
>>>
>>> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
>>> ---
>>>  drivers/phy/phy-core.c  | 62 ++++++++++++++++++++++++++++++++++++++++++-
>>>  include/linux/phy/phy.h | 42 ++++++++++++++++++++++++++++-
>>>  2 files changed, 104 insertions(+)
>>>
>>> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
>>> index 35fd38c5a4a1..6eaf655e370f 100644
>>> --- a/drivers/phy/phy-core.c
>>> +++ b/drivers/phy/phy-core.c
>>> @@ -408,6 +408,68 @@ int phy_calibrate(struct phy *phy)
>>>  EXPORT_SYMBOL_GPL(phy_calibrate);
>>>  
>>>  /**
>>> + * phy_configure() - Changes the phy parameters
>>> + * @phy: the phy returned by phy_get()
>>> + * @mode: phy_mode the configuration is applicable to.
>>
>> mode should be used if the same PHY can be configured in multiple modes. But
>> with phy_set_mode() and phy_calibrate() we could achieve the same.
> 
> So you would change the prototype to have a configuration applying
> only to the current mode set previously through set_mode?

yeah.
With phy_configure, if the PHY is not in @mode, it should return an error? Or
will it set the PHY to @mode and apply the configuration in @opts?
> 
> Can we have PHY that operate in multiple modes at the same time?

Not at the same time. But the same PHY can operate in multiple modes (For
example we have PHYs that can be used either with PCIe or USB3)
> 
>>> + * @opts: New configuration to apply
>>
>> Should these configuration come from the consumer driver?
> 
> Yes

How does the consumer driver get these configurations? Is it from user space or
dt associated with consumer device.
> 
>> Can't the helper functions be directly invoked by the PHY driver for
>> the configuration.
> 
> Not really. The helpers are here to introduce functions that give you
> the defaults provided by the spec for a given configuration, and to
> validate that a given configuration is within the spec boundaries. I
> expect some consumers to need to change the defaults for some more
> suited parameters that are still within the boundaries defined by the
> spec.
> 
> And I'd really want to have that interface being quite generic, and
> applicable to other phy modes as well. The allwinner USB PHY for
> example require at the moment an extra function that could be moved to
> this API:
> https://elixir.bootlin.com/linux/latest/source/drivers/phy/allwinner/phy-sun4i-usb.c#L512
> 
>>> + *
>>> + * Used to change the PHY parameters. phy_init() must have
>>> + * been called on the phy.
>>> + *
>>> + * Returns: 0 if successful, an negative error code otherwise
>>> + */
>>> +int phy_configure(struct phy *phy, enum phy_mode mode,
>>> +		  union phy_configure_opts *opts)
>>> +{> +	int ret;
>>> +
>>> +	if (!phy)
>>> +		return -EINVAL;
>>> +
>>> +	if (!phy->ops->configure)
>>> +		return 0;
>>> +
>>> +	mutex_lock(&phy->mutex);
>>> +	ret = phy->ops->configure(phy, mode, opts);
>>> +	mutex_unlock(&phy->mutex);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/**
>>> + * phy_validate() - Checks the phy parameters
>>> + * @phy: the phy returned by phy_get()
>>> + * @mode: phy_mode the configuration is applicable to.
>>> + * @opts: Configuration to check
>>> + *
>>> + * Used to check that the current set of parameters can be handled by
>>> + * the phy. Implementations are free to tune the parameters passed as
>>> + * arguments if needed by some implementation detail or
>>> + * constraints. It will not change any actual configuration of the
>>> + * PHY, so calling it as many times as deemed fit will have no side
>>> + * effect.
>>> + *
>>> + * Returns: 0 if successful, an negative error code otherwise
>>> + */
>>> +int phy_validate(struct phy *phy, enum phy_mode mode,
>>> +		  union phy_configure_opts *opts)
>>
>> IIUC the consumer driver will pass configuration options (or PHY parameters)
>> which will be validated by the PHY driver and in some cases the PHY driver can
>> modify the configuration options? And these modified configuration options will
>> again be given to phy_configure?
>>
>> Looks like it's a round about way of doing the same thing.
> 
> Not really. The validate callback allows to check whether a particular
> configuration would work, and try to negotiate a set of configurations
> that both the consumer and the PHY could work with.

Maybe the PHY should provide the list of supported features to the consumer
driver and the consumer should select a supported feature?

> 
> For example, DRM requires this to filter out display modes (ie,
> resolutions) that wouldn't be achievable by the PHY so that it's never

Can't the consumer driver just tell the required resolution to the PHY and PHY
figuring out all the parameters for the resolution or an error if that
resolution cannot be supported?

Thanks
Kishon

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-12  7:42       ` Kishon Vijay Abraham I
@ 2018-09-12  8:42         ` Maxime Ripard
  2018-09-14  8:48           ` Kishon Vijay Abraham I
  0 siblings, 1 reply; 40+ messages in thread
From: Maxime Ripard @ 2018-09-12  8:42 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Boris Brezillon, Thomas Petazzoni, Laurent Pinchart, linux-media,
	Archit Taneja, Andrzej Hajda, Chen-Yu Tsai, linux-kernel,
	dri-devel, linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

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

Hi!

On Wed, Sep 12, 2018 at 01:12:31PM +0530, Kishon Vijay Abraham I wrote:
> On Thursday 06 September 2018 08:26 PM, Maxime Ripard wrote:
> > Hi Kishon,
> > 
> > On Thu, Sep 06, 2018 at 02:57:58PM +0530, Kishon Vijay Abraham I wrote:
> >> On Wednesday 05 September 2018 02:46 PM, Maxime Ripard wrote:
> >>> The phy framework is only allowing to configure the power state of the PHY
> >>> using the init and power_on hooks, and their power_off and exit
> >>> counterparts.
> >>>
> >>> While it works for most, simple, PHYs supported so far, some more advanced
> >>> PHYs need some configuration depending on runtime parameters. These PHYs
> >>> have been supported by a number of means already, often by using ad-hoc
> >>> drivers in their consumer drivers.
> >>>
> >>> That doesn't work too well however, when a consumer device needs to deal
> >>> multiple PHYs, or when multiple consumers need to deal with the same PHY (a
> >>> DSI driver and a CSI driver for example).
> >>>
> >>> So we'll add a new interface, through two funtions, phy_validate and
> >>> phy_configure. The first one will allow to check that a current
> >>> configuration, for a given mode, is applicable. It will also allow the PHY
> >>> driver to tune the settings given as parameters as it sees fit.
> >>>
> >>> phy_configure will actually apply that configuration in the phy itself.
> >>>
> >>> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
> >>> ---
> >>>  drivers/phy/phy-core.c  | 62 ++++++++++++++++++++++++++++++++++++++++++-
> >>>  include/linux/phy/phy.h | 42 ++++++++++++++++++++++++++++-
> >>>  2 files changed, 104 insertions(+)
> >>>
> >>> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> >>> index 35fd38c5a4a1..6eaf655e370f 100644
> >>> --- a/drivers/phy/phy-core.c
> >>> +++ b/drivers/phy/phy-core.c
> >>> @@ -408,6 +408,68 @@ int phy_calibrate(struct phy *phy)
> >>>  EXPORT_SYMBOL_GPL(phy_calibrate);
> >>>  
> >>>  /**
> >>> + * phy_configure() - Changes the phy parameters
> >>> + * @phy: the phy returned by phy_get()
> >>> + * @mode: phy_mode the configuration is applicable to.
> >>
> >> mode should be used if the same PHY can be configured in multiple modes. But
> >> with phy_set_mode() and phy_calibrate() we could achieve the same.
> > 
> > So you would change the prototype to have a configuration applying
> > only to the current mode set previously through set_mode?
> 
> yeah.
> With phy_configure, if the PHY is not in @mode, it should return an error? Or
> will it set the PHY to @mode and apply the configuration in @opts?

I wanted to have it return an error either if it was configured in
another mode or if the mode was unsupported yes.

> > Can we have PHY that operate in multiple modes at the same time?
> 
> Not at the same time. But the same PHY can operate in multiple modes (For
> example we have PHYs that can be used either with PCIe or USB3)

Ok, that makes sense. I guess we could rely on phy_set_mode then if
you prefer.

> >>> + * @opts: New configuration to apply
> >>
> >> Should these configuration come from the consumer driver?
> > 
> > Yes
> 
> How does the consumer driver get these configurations? Is it from user space or
> dt associated with consumer device.

It really depends on multiple factors (and I guess on what mode the
PHY is actually supposed to support), but in the case covered by this
serie, the info mostly come from multiple places:
  - The resolutions supported by the panel
  - The resolutions supported by the phy consumer (and its
    integration, for things like the clock rates it can output)
  - The resolutions and timings supported by the phy itself (once
    again, the integration is mostly involved here since it really
    only depends on which clock rates can be achieved)
  - The timings boundaries that the specification has
  - The resolution selected by the user

So we'd have that information coming from multiple places: the
userspace would select the resolution, drivers would be able to filter
out unsupported resolutions, and the DT will provide the integration
details to help them do so.

But I guess from an API standpoint, it really is expected to be
assembled by the phy consumer driver.

> >>> +/**
> >>> + * phy_validate() - Checks the phy parameters
> >>> + * @phy: the phy returned by phy_get()
> >>> + * @mode: phy_mode the configuration is applicable to.
> >>> + * @opts: Configuration to check
> >>> + *
> >>> + * Used to check that the current set of parameters can be handled by
> >>> + * the phy. Implementations are free to tune the parameters passed as
> >>> + * arguments if needed by some implementation detail or
> >>> + * constraints. It will not change any actual configuration of the
> >>> + * PHY, so calling it as many times as deemed fit will have no side
> >>> + * effect.
> >>> + *
> >>> + * Returns: 0 if successful, an negative error code otherwise
> >>> + */
> >>> +int phy_validate(struct phy *phy, enum phy_mode mode,
> >>> +		  union phy_configure_opts *opts)
> >>
> >> IIUC the consumer driver will pass configuration options (or PHY parameters)
> >> which will be validated by the PHY driver and in some cases the PHY driver can
> >> modify the configuration options? And these modified configuration options will
> >> again be given to phy_configure?
> >>
> >> Looks like it's a round about way of doing the same thing.
> > 
> > Not really. The validate callback allows to check whether a particular
> > configuration would work, and try to negotiate a set of configurations
> > that both the consumer and the PHY could work with.
> 
> Maybe the PHY should provide the list of supported features to the consumer
> driver and the consumer should select a supported feature?

It's not really about the features it supports, but the boundaries it
might have on those features. For example, the same phy integrated in
two different SoCs will probably have some limit on the clock rate it
can output because of the phy design itself, but also because of the
clock that is fed into that phy, and that will be different from one
SoC to the other.

This integration will prevent us to use some clock rates on the first
SoC, while the second one would be totally fine with it.

Obviously, the consumer driver shouldn't care about the phy
integration details, especially since some of those consumer drivers
need to interact with multiple phy designs (or the same phy design can
be used by multiple consumers).

So knowing that a feature is supported is really not enough.

With MIPI-DPHY at least, the API is generic enough so that another
mode where the features would make sense could implement a feature
flag if that makes sense.

> > For example, DRM requires this to filter out display modes (ie,
> > resolutions) that wouldn't be achievable by the PHY so that it's never
> 
> Can't the consumer driver just tell the required resolution to the PHY and PHY
> figuring out all the parameters for the resolution or an error if that
> resolution cannot be supported?

Not really either. With MIPI D-PHY, the phy is fed a clock that is
generated by the phy consumer, which might or might not be an exact
fit for the resolution. There's so many resolutions that in most case,
the clock factors don't allow you to have a perfect match. And
obviously, this imprecision should be taken into account by the PHY as
well.

And then, there's also the matter than due to design constraints, some
consumers would have fixed timings that are not at the spec default
value, but still within the acceptable range. We need to communicate
that to the PHY.

Maxime

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

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

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-12  8:42         ` Maxime Ripard
@ 2018-09-14  8:48           ` Kishon Vijay Abraham I
  2018-09-19 12:14             ` Maxime Ripard
  0 siblings, 1 reply; 40+ messages in thread
From: Kishon Vijay Abraham I @ 2018-09-14  8:48 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Boris Brezillon, Thomas Petazzoni, Laurent Pinchart, linux-media,
	Archit Taneja, Andrzej Hajda, Chen-Yu Tsai, linux-kernel,
	dri-devel, linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

Hi Maxime,

On Wednesday 12 September 2018 02:12 PM, Maxime Ripard wrote:
> Hi!
> 
> On Wed, Sep 12, 2018 at 01:12:31PM +0530, Kishon Vijay Abraham I wrote:
>> On Thursday 06 September 2018 08:26 PM, Maxime Ripard wrote:
>>> Hi Kishon,
>>>
>>> On Thu, Sep 06, 2018 at 02:57:58PM +0530, Kishon Vijay Abraham I wrote:
>>>> On Wednesday 05 September 2018 02:46 PM, Maxime Ripard wrote:
>>>>> The phy framework is only allowing to configure the power state of the PHY
>>>>> using the init and power_on hooks, and their power_off and exit
>>>>> counterparts.
>>>>>
>>>>> While it works for most, simple, PHYs supported so far, some more advanced
>>>>> PHYs need some configuration depending on runtime parameters. These PHYs
>>>>> have been supported by a number of means already, often by using ad-hoc
>>>>> drivers in their consumer drivers.
>>>>>
>>>>> That doesn't work too well however, when a consumer device needs to deal
>>>>> multiple PHYs, or when multiple consumers need to deal with the same PHY (a
>>>>> DSI driver and a CSI driver for example).
>>>>>
>>>>> So we'll add a new interface, through two funtions, phy_validate and
>>>>> phy_configure. The first one will allow to check that a current
>>>>> configuration, for a given mode, is applicable. It will also allow the PHY
>>>>> driver to tune the settings given as parameters as it sees fit.
>>>>>
>>>>> phy_configure will actually apply that configuration in the phy itself.
>>>>>
>>>>> Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
>>>>> ---
>>>>>  drivers/phy/phy-core.c  | 62 ++++++++++++++++++++++++++++++++++++++++++-
>>>>>  include/linux/phy/phy.h | 42 ++++++++++++++++++++++++++++-
>>>>>  2 files changed, 104 insertions(+)
>>>>>
>>>>> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
>>>>> index 35fd38c5a4a1..6eaf655e370f 100644
>>>>> --- a/drivers/phy/phy-core.c
>>>>> +++ b/drivers/phy/phy-core.c
>>>>> @@ -408,6 +408,68 @@ int phy_calibrate(struct phy *phy)
>>>>>  EXPORT_SYMBOL_GPL(phy_calibrate);
>>>>>  
>>>>>  /**
>>>>> + * phy_configure() - Changes the phy parameters
>>>>> + * @phy: the phy returned by phy_get()
>>>>> + * @mode: phy_mode the configuration is applicable to.
>>>>
>>>> mode should be used if the same PHY can be configured in multiple modes. But
>>>> with phy_set_mode() and phy_calibrate() we could achieve the same.
>>>
>>> So you would change the prototype to have a configuration applying
>>> only to the current mode set previously through set_mode?
>>
>> yeah.
>> With phy_configure, if the PHY is not in @mode, it should return an error? Or
>> will it set the PHY to @mode and apply the configuration in @opts?
> 
> I wanted to have it return an error either if it was configured in
> another mode or if the mode was unsupported yes.
> 
>>> Can we have PHY that operate in multiple modes at the same time?
>>
>> Not at the same time. But the same PHY can operate in multiple modes (For
>> example we have PHYs that can be used either with PCIe or USB3)
> 
> Ok, that makes sense. I guess we could rely on phy_set_mode then if
> you prefer.
> 
>>>>> + * @opts: New configuration to apply
>>>>
>>>> Should these configuration come from the consumer driver?
>>>
>>> Yes
>>
>> How does the consumer driver get these configurations? Is it from user space or
>> dt associated with consumer device.
> 
> It really depends on multiple factors (and I guess on what mode the
> PHY is actually supposed to support), but in the case covered by this
> serie, the info mostly come from multiple places:
>   - The resolutions supported by the panel
>   - The resolutions supported by the phy consumer (and its
>     integration, for things like the clock rates it can output)
>   - The resolutions and timings supported by the phy itself (once
>     again, the integration is mostly involved here since it really
>     only depends on which clock rates can be achieved)
>   - The timings boundaries that the specification has
>   - The resolution selected by the user
> 
> So we'd have that information coming from multiple places: the
> userspace would select the resolution, drivers would be able to filter
> out unsupported resolutions, and the DT will provide the integration
> details to help them do so.
> 
> But I guess from an API standpoint, it really is expected to be
> assembled by the phy consumer driver.
> 
>>>>> +/**
>>>>> + * phy_validate() - Checks the phy parameters
>>>>> + * @phy: the phy returned by phy_get()
>>>>> + * @mode: phy_mode the configuration is applicable to.
>>>>> + * @opts: Configuration to check
>>>>> + *
>>>>> + * Used to check that the current set of parameters can be handled by
>>>>> + * the phy. Implementations are free to tune the parameters passed as
>>>>> + * arguments if needed by some implementation detail or
>>>>> + * constraints. It will not change any actual configuration of the
>>>>> + * PHY, so calling it as many times as deemed fit will have no side
>>>>> + * effect.
>>>>> + *
>>>>> + * Returns: 0 if successful, an negative error code otherwise
>>>>> + */
>>>>> +int phy_validate(struct phy *phy, enum phy_mode mode,
>>>>> +		  union phy_configure_opts *opts)
>>>>
>>>> IIUC the consumer driver will pass configuration options (or PHY parameters)
>>>> which will be validated by the PHY driver and in some cases the PHY driver can
>>>> modify the configuration options? And these modified configuration options will
>>>> again be given to phy_configure?
>>>>
>>>> Looks like it's a round about way of doing the same thing.
>>>
>>> Not really. The validate callback allows to check whether a particular
>>> configuration would work, and try to negotiate a set of configurations
>>> that both the consumer and the PHY could work with.
>>
>> Maybe the PHY should provide the list of supported features to the consumer
>> driver and the consumer should select a supported feature?
> 
> It's not really about the features it supports, but the boundaries it
> might have on those features. For example, the same phy integrated in
> two different SoCs will probably have some limit on the clock rate it
> can output because of the phy design itself, but also because of the
> clock that is fed into that phy, and that will be different from one
> SoC to the other.
> 
> This integration will prevent us to use some clock rates on the first
> SoC, while the second one would be totally fine with it.

If there's a clock that is fed to the PHY from the consumer, then the consumer
driver should model a clock provider and the PHY can get a reference to it
using clk_get(). Rockchip and Arasan eMMC PHYs has already used something like
that.

Assuming the PHY can get a reference to the clock provided by the consumer,
what are the parameters we'll be able to get rid of in struct
phy_configure_opts_mipi_dphy?

I'm sorry but I'm not convinced a consumer driver should have all the details
that are added in phy_configure_opts_mipi_dphy.
> 
> Obviously, the consumer driver shouldn't care about the phy
> integration details, especially since some of those consumer drivers
> need to interact with multiple phy designs (or the same phy design can
> be used by multiple consumers).
> 
> So knowing that a feature is supported is really not enough.
> 
> With MIPI-DPHY at least, the API is generic enough so that another
> mode where the features would make sense could implement a feature
> flag if that makes sense.
> 
>>> For example, DRM requires this to filter out display modes (ie,
>>> resolutions) that wouldn't be achievable by the PHY so that it's never
>>
>> Can't the consumer driver just tell the required resolution to the PHY and PHY
>> figuring out all the parameters for the resolution or an error if that
>> resolution cannot be supported?
> 
> Not really either. With MIPI D-PHY, the phy is fed a clock that is
> generated by the phy consumer, which might or might not be an exact
> fit for the resolution. There's so many resolutions that in most case,
> the clock factors don't allow you to have a perfect match. And
> obviously, this imprecision should be taken into account by the PHY as
> well.
> 
> And then, there's also the matter than due to design constraints, some
> consumers would have fixed timings that are not at the spec default
> value, but still within the acceptable range. We need to communicate
> that to the PHY.

Here do you mean videomode timings?

Thanks
Kishon

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-14  8:48           ` Kishon Vijay Abraham I
@ 2018-09-19 12:14             ` Maxime Ripard
  2018-09-21 14:18               ` Maxime Ripard
  2018-09-24  8:48               ` Kishon Vijay Abraham I
  0 siblings, 2 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-19 12:14 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Boris Brezillon, Thomas Petazzoni, Laurent Pinchart, linux-media,
	Archit Taneja, Andrzej Hajda, Chen-Yu Tsai, linux-kernel,
	dri-devel, linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

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

Hi,

On Fri, Sep 14, 2018 at 02:18:37PM +0530, Kishon Vijay Abraham I wrote:
> >>>>> +/**
> >>>>> + * phy_validate() - Checks the phy parameters
> >>>>> + * @phy: the phy returned by phy_get()
> >>>>> + * @mode: phy_mode the configuration is applicable to.
> >>>>> + * @opts: Configuration to check
> >>>>> + *
> >>>>> + * Used to check that the current set of parameters can be handled by
> >>>>> + * the phy. Implementations are free to tune the parameters passed as
> >>>>> + * arguments if needed by some implementation detail or
> >>>>> + * constraints. It will not change any actual configuration of the
> >>>>> + * PHY, so calling it as many times as deemed fit will have no side
> >>>>> + * effect.
> >>>>> + *
> >>>>> + * Returns: 0 if successful, an negative error code otherwise
> >>>>> + */
> >>>>> +int phy_validate(struct phy *phy, enum phy_mode mode,
> >>>>> +		  union phy_configure_opts *opts)
> >>>>
> >>>> IIUC the consumer driver will pass configuration options (or PHY parameters)
> >>>> which will be validated by the PHY driver and in some cases the PHY driver can
> >>>> modify the configuration options? And these modified configuration options will
> >>>> again be given to phy_configure?
> >>>>
> >>>> Looks like it's a round about way of doing the same thing.
> >>>
> >>> Not really. The validate callback allows to check whether a particular
> >>> configuration would work, and try to negotiate a set of configurations
> >>> that both the consumer and the PHY could work with.
> >>
> >> Maybe the PHY should provide the list of supported features to the consumer
> >> driver and the consumer should select a supported feature?
> > 
> > It's not really about the features it supports, but the boundaries it
> > might have on those features. For example, the same phy integrated in
> > two different SoCs will probably have some limit on the clock rate it
> > can output because of the phy design itself, but also because of the
> > clock that is fed into that phy, and that will be different from one
> > SoC to the other.
> > 
> > This integration will prevent us to use some clock rates on the first
> > SoC, while the second one would be totally fine with it.
> 
> If there's a clock that is fed to the PHY from the consumer, then the consumer
> driver should model a clock provider and the PHY can get a reference to it
> using clk_get(). Rockchip and Arasan eMMC PHYs has already used something like
> that.

That would be doable, but no current driver has had this in their
binding. So that would prevent any further rework, and make that whole
series moot. And while I could live without the Allwinner part, the
Cadence one is really needed.

> Assuming the PHY can get a reference to the clock provided by the consumer,
> what are the parameters we'll be able to get rid of in struct
> phy_configure_opts_mipi_dphy?

hs_clock_rate and lp_clock_rate. All the other ones are needed.

> I'm sorry but I'm not convinced a consumer driver should have all the details
> that are added in phy_configure_opts_mipi_dphy.

If it can convince you, here is the parameters that are needed by all
the MIPI-DSI drivers currently in Linux to configure their PHY:

  - cdns-dsi (drivers/gpu/drm/bridge/cdns-dsi.c)
    - hs_clk_rate
    - lanes
    - videomode

  - kirin (drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c)
    - hs_exit
    - hs_prepare
    - hs_trail
    - hs_zero
    - lpx
    - ta_get
    - ta_go
    - wakeup

  - msm (drivers/gpu/drm/msm/dsi/*)
    - clk_post
    - clk_pre
    - clk_prepare
    - clk_trail
    - clk_zero
    - hs_clk_rate
    - hs_exit
    - hs_prepare
    - hs_trail
    - hs_zero
    - lp_clk_rate
    - ta_get
    - ta_go
    - ta_sure

  - mtk (drivers/gpu/drm/mediatek/mtk_dsi.c)
    - hs_clk_rate
    - hs_exit
    - hs_prepare
    - hs_trail
    - hs_zero
    - lpx

  - sun4i (drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c)
    - clk_post
    - clk_pre
    - clk_prepare
    - clk_zero
    - hs_prepare
    - hs_trail
    - lanes
    - lp_clk_rate

  - tegra (drivers/gpu/drm/tegra/dsi.c)
    - clk_post
    - clk_pre
    - clk_prepare
    - clk_trail
    - clk_zero
    - hs_exit
    - hs_prepare
    - hs_trail
    - hs_zero
    - lpx
    - ta_get
    - ta_go
    - ta_sure

  - vc4 (drivers/gpu/drm/vc4/vc4_dsi.c)
    - hs_clk_rate
    - lanes

Now, for MIPI-CSI receivers:

  - marvell-ccic (drivers/media/platform/marvell-ccic/mcam-core.c)
    - clk_term_en
    - clk_settle
    - d_term_en
    - hs_settle
    - lp_clk_rate

  - omap4iss (drivers/staging/media/omap4iss/iss_csiphy.c)
    - clk_miss
    - clk_settle
    - clk_term
    - hs_settle
    - hs_term
    - lanes

  - rcar-vin (drivers/media/platform/rcar-vin/rcar-csi2.c)
    - hs_clk_rate
    - lanes

  - ti-vpe (drivers/media/platform/ti-vpe/cal.c)
    - clk_term_en
    - d_term_en
    - hs_settle
    - hs_term

So the timings expressed in the structure are the set of all the ones
currently used in the tree by DSI and CSI drivers. I would consider
that a good proof that it would be useful.

Note that at least cdns-dsi, exynos4-is
(drivers/media/platform/exynos4-is/mipi-csis.c), kirin, sun4i, msm,
mtk, omap4iss, plus the v4l2 drivers cdns-csi2tx and cdns-csi2rx I
want to convert, have already either a driver for their DPHY using the
phy framework plus a configuration function, or a design very similar
that could be migrated to such an API.

> > Obviously, the consumer driver shouldn't care about the phy
> > integration details, especially since some of those consumer drivers
> > need to interact with multiple phy designs (or the same phy design can
> > be used by multiple consumers).
> > 
> > So knowing that a feature is supported is really not enough.
> > 
> > With MIPI-DPHY at least, the API is generic enough so that another
> > mode where the features would make sense could implement a feature
> > flag if that makes sense.
> > 
> >>> For example, DRM requires this to filter out display modes (ie,
> >>> resolutions) that wouldn't be achievable by the PHY so that it's never
> >>
> >> Can't the consumer driver just tell the required resolution to the PHY and PHY
> >> figuring out all the parameters for the resolution or an error if that
> >> resolution cannot be supported?
> > 
> > Not really either. With MIPI D-PHY, the phy is fed a clock that is
> > generated by the phy consumer, which might or might not be an exact
> > fit for the resolution. There's so many resolutions that in most case,
> > the clock factors don't allow you to have a perfect match. And
> > obviously, this imprecision should be taken into account by the PHY as
> > well.
> > 
> > And then, there's also the matter than due to design constraints, some
> > consumers would have fixed timings that are not at the spec default
> > value, but still within the acceptable range. We need to communicate
> > that to the PHY.
> 
> Here do you mean videomode timings?

No, I mean the DPHY timings.

Maxime

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

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

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-19 12:14             ` Maxime Ripard
@ 2018-09-21 14:18               ` Maxime Ripard
  2018-09-24  8:48               ` Kishon Vijay Abraham I
  1 sibling, 0 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-21 14:18 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Boris Brezillon, Thomas Petazzoni, Laurent Pinchart, linux-media,
	Archit Taneja, Andrzej Hajda, Chen-Yu Tsai, linux-kernel,
	dri-devel, linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

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

On Wed, Sep 19, 2018 at 02:14:36PM +0200, Maxime Ripard wrote:
> > I'm sorry but I'm not convinced a consumer driver should have all the details
> > that are added in phy_configure_opts_mipi_dphy.
> 
> If it can convince you, here is the parameters that are needed by all
> the MIPI-DSI drivers currently in Linux to configure their PHY:
> 
>   - cdns-dsi (drivers/gpu/drm/bridge/cdns-dsi.c)
>     - hs_clk_rate
>     - lanes
>     - videomode
> 
>   - kirin (drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c)
>     - hs_exit
>     - hs_prepare
>     - hs_trail
>     - hs_zero
>     - lpx
>     - ta_get
>     - ta_go
>     - wakeup
> 
>   - msm (drivers/gpu/drm/msm/dsi/*)
>     - clk_post
>     - clk_pre
>     - clk_prepare
>     - clk_trail
>     - clk_zero
>     - hs_clk_rate
>     - hs_exit
>     - hs_prepare
>     - hs_trail
>     - hs_zero
>     - lp_clk_rate
>     - ta_get
>     - ta_go
>     - ta_sure
> 
>   - mtk (drivers/gpu/drm/mediatek/mtk_dsi.c)
>     - hs_clk_rate
>     - hs_exit
>     - hs_prepare
>     - hs_trail
>     - hs_zero
>     - lpx
> 
>   - sun4i (drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c)
>     - clk_post
>     - clk_pre
>     - clk_prepare
>     - clk_zero
>     - hs_prepare
>     - hs_trail
>     - lanes
>     - lp_clk_rate
> 
>   - tegra (drivers/gpu/drm/tegra/dsi.c)
>     - clk_post
>     - clk_pre
>     - clk_prepare
>     - clk_trail
>     - clk_zero
>     - hs_exit
>     - hs_prepare
>     - hs_trail
>     - hs_zero
>     - lpx
>     - ta_get
>     - ta_go
>     - ta_sure
> 
>   - vc4 (drivers/gpu/drm/vc4/vc4_dsi.c)
>     - hs_clk_rate
>     - lanes
> 
> Now, for MIPI-CSI receivers:
> 
>   - marvell-ccic (drivers/media/platform/marvell-ccic/mcam-core.c)
>     - clk_term_en
>     - clk_settle
>     - d_term_en
>     - hs_settle
>     - lp_clk_rate
> 
>   - omap4iss (drivers/staging/media/omap4iss/iss_csiphy.c)
>     - clk_miss
>     - clk_settle
>     - clk_term
>     - hs_settle
>     - hs_term
>     - lanes
> 
>   - rcar-vin (drivers/media/platform/rcar-vin/rcar-csi2.c)
>     - hs_clk_rate
>     - lanes
> 
>   - ti-vpe (drivers/media/platform/ti-vpe/cal.c)
>     - clk_term_en
>     - d_term_en
>     - hs_settle
>     - hs_term
> 
> So the timings expressed in the structure are the set of all the ones
> currently used in the tree by DSI and CSI drivers. I would consider
> that a good proof that it would be useful.
> 
> Note that at least cdns-dsi, exynos4-is
> (drivers/media/platform/exynos4-is/mipi-csis.c), kirin, sun4i, msm,
> mtk, omap4iss, plus the v4l2 drivers cdns-csi2tx and cdns-csi2rx I
> want to convert, have already either a driver for their DPHY using the
> phy framework plus a configuration function, or a design very similar
> that could be migrated to such an API.

There's also a patch set currently being submitted that uses a phy
driver + custom functions:
https://lore.kernel.org/patchwork/cover/988959/

Maxime

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

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

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-19 12:14             ` Maxime Ripard
  2018-09-21 14:18               ` Maxime Ripard
@ 2018-09-24  8:48               ` Kishon Vijay Abraham I
  2018-09-24  9:54                 ` Maxime Ripard
  1 sibling, 1 reply; 40+ messages in thread
From: Kishon Vijay Abraham I @ 2018-09-24  8:48 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Boris Brezillon, Thomas Petazzoni, Laurent Pinchart, linux-media,
	Archit Taneja, Andrzej Hajda, Chen-Yu Tsai, linux-kernel,
	dri-devel, linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

Hi Maxime,

On Wednesday 19 September 2018 05:44 PM, Maxime Ripard wrote:
> Hi,
> 
> On Fri, Sep 14, 2018 at 02:18:37PM +0530, Kishon Vijay Abraham I wrote:
>>>>>>> +/**
>>>>>>> + * phy_validate() - Checks the phy parameters
>>>>>>> + * @phy: the phy returned by phy_get()
>>>>>>> + * @mode: phy_mode the configuration is applicable to.
>>>>>>> + * @opts: Configuration to check
>>>>>>> + *
>>>>>>> + * Used to check that the current set of parameters can be handled by
>>>>>>> + * the phy. Implementations are free to tune the parameters passed as
>>>>>>> + * arguments if needed by some implementation detail or
>>>>>>> + * constraints. It will not change any actual configuration of the
>>>>>>> + * PHY, so calling it as many times as deemed fit will have no side
>>>>>>> + * effect.
>>>>>>> + *
>>>>>>> + * Returns: 0 if successful, an negative error code otherwise
>>>>>>> + */
>>>>>>> +int phy_validate(struct phy *phy, enum phy_mode mode,
>>>>>>> +		  union phy_configure_opts *opts)
>>>>>>
>>>>>> IIUC the consumer driver will pass configuration options (or PHY parameters)
>>>>>> which will be validated by the PHY driver and in some cases the PHY driver can
>>>>>> modify the configuration options? And these modified configuration options will
>>>>>> again be given to phy_configure?
>>>>>>
>>>>>> Looks like it's a round about way of doing the same thing.
>>>>>
>>>>> Not really. The validate callback allows to check whether a particular
>>>>> configuration would work, and try to negotiate a set of configurations
>>>>> that both the consumer and the PHY could work with.
>>>>
>>>> Maybe the PHY should provide the list of supported features to the consumer
>>>> driver and the consumer should select a supported feature?
>>>
>>> It's not really about the features it supports, but the boundaries it
>>> might have on those features. For example, the same phy integrated in
>>> two different SoCs will probably have some limit on the clock rate it
>>> can output because of the phy design itself, but also because of the
>>> clock that is fed into that phy, and that will be different from one
>>> SoC to the other.
>>>
>>> This integration will prevent us to use some clock rates on the first
>>> SoC, while the second one would be totally fine with it.
>>
>> If there's a clock that is fed to the PHY from the consumer, then the consumer
>> driver should model a clock provider and the PHY can get a reference to it
>> using clk_get(). Rockchip and Arasan eMMC PHYs has already used something like
>> that.
> 
> That would be doable, but no current driver has had this in their
> binding. So that would prevent any further rework, and make that whole
> series moot. And while I could live without the Allwinner part, the
> Cadence one is really needed.

We could add a binding and modify the driver to to register a clock provider.
That could be included in this series itself.
> 
>> Assuming the PHY can get a reference to the clock provided by the consumer,
>> what are the parameters we'll be able to get rid of in struct
>> phy_configure_opts_mipi_dphy?
> 
> hs_clock_rate and lp_clock_rate. All the other ones are needed.

For a start we could use that and get rid of hs_clock_rate and lp_clock_rate in
phy_configure_opts_mipi_dphy.

We could also use phy_set_bus_width() for lanes.
> 
>> I'm sorry but I'm not convinced a consumer driver should have all the details
>> that are added in phy_configure_opts_mipi_dphy.
> 
> If it can convince you, here is the parameters that are needed by all
> the MIPI-DSI drivers currently in Linux to configure their PHY:
> 
>   - cdns-dsi (drivers/gpu/drm/bridge/cdns-dsi.c)
>     - hs_clk_rate
>     - lanes
>     - videomode
> 
>   - kirin (drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c)
>     - hs_exit
>     - hs_prepare
>     - hs_trail
>     - hs_zero
>     - lpx
>     - ta_get
>     - ta_go
>     - wakeup
> 
>   - msm (drivers/gpu/drm/msm/dsi/*)
>     - clk_post
>     - clk_pre
>     - clk_prepare
>     - clk_trail
>     - clk_zero
>     - hs_clk_rate
>     - hs_exit
>     - hs_prepare
>     - hs_trail
>     - hs_zero
>     - lp_clk_rate
>     - ta_get
>     - ta_go
>     - ta_sure
> 
>   - mtk (drivers/gpu/drm/mediatek/mtk_dsi.c)
>     - hs_clk_rate
>     - hs_exit
>     - hs_prepare
>     - hs_trail
>     - hs_zero
>     - lpx
> 
>   - sun4i (drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c)
>     - clk_post
>     - clk_pre
>     - clk_prepare
>     - clk_zero
>     - hs_prepare
>     - hs_trail
>     - lanes
>     - lp_clk_rate
> 
>   - tegra (drivers/gpu/drm/tegra/dsi.c)
>     - clk_post
>     - clk_pre
>     - clk_prepare
>     - clk_trail
>     - clk_zero
>     - hs_exit
>     - hs_prepare
>     - hs_trail
>     - hs_zero
>     - lpx
>     - ta_get
>     - ta_go
>     - ta_sure
> 
>   - vc4 (drivers/gpu/drm/vc4/vc4_dsi.c)
>     - hs_clk_rate
>     - lanes
> 
> Now, for MIPI-CSI receivers:
> 
>   - marvell-ccic (drivers/media/platform/marvell-ccic/mcam-core.c)
>     - clk_term_en
>     - clk_settle
>     - d_term_en
>     - hs_settle
>     - lp_clk_rate
> 
>   - omap4iss (drivers/staging/media/omap4iss/iss_csiphy.c)
>     - clk_miss
>     - clk_settle
>     - clk_term
>     - hs_settle
>     - hs_term
>     - lanes
> 
>   - rcar-vin (drivers/media/platform/rcar-vin/rcar-csi2.c)
>     - hs_clk_rate
>     - lanes
> 
>   - ti-vpe (drivers/media/platform/ti-vpe/cal.c)
>     - clk_term_en
>     - d_term_en
>     - hs_settle
>     - hs_term

Thank you for providing the exhaustive list.
> 
> So the timings expressed in the structure are the set of all the ones
> currently used in the tree by DSI and CSI drivers. I would consider
> that a good proof that it would be useful.

The problem I see here is each platform (PHY) will have it's own set of
parameters and we have to keep adding members to phy_configure_opts which is
not scalable. We should try to find a correlation between generic PHY modes and
these parameters (at-least for a subset).

Thanks
Kishon

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-24  8:48               ` Kishon Vijay Abraham I
@ 2018-09-24  9:54                 ` Maxime Ripard
  2018-09-24 11:55                   ` Kishon Vijay Abraham I
  0 siblings, 1 reply; 40+ messages in thread
From: Maxime Ripard @ 2018-09-24  9:54 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Boris Brezillon, Thomas Petazzoni, Laurent Pinchart, linux-media,
	Archit Taneja, Andrzej Hajda, Chen-Yu Tsai, linux-kernel,
	dri-devel, linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

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

Hi,

On Mon, Sep 24, 2018 at 02:18:35PM +0530, Kishon Vijay Abraham I wrote:
> On Wednesday 19 September 2018 05:44 PM, Maxime Ripard wrote:
> > Hi,
> > 
> > On Fri, Sep 14, 2018 at 02:18:37PM +0530, Kishon Vijay Abraham I wrote:
> >>>>>>> +/**
> >>>>>>> + * phy_validate() - Checks the phy parameters
> >>>>>>> + * @phy: the phy returned by phy_get()
> >>>>>>> + * @mode: phy_mode the configuration is applicable to.
> >>>>>>> + * @opts: Configuration to check
> >>>>>>> + *
> >>>>>>> + * Used to check that the current set of parameters can be handled by
> >>>>>>> + * the phy. Implementations are free to tune the parameters passed as
> >>>>>>> + * arguments if needed by some implementation detail or
> >>>>>>> + * constraints. It will not change any actual configuration of the
> >>>>>>> + * PHY, so calling it as many times as deemed fit will have no side
> >>>>>>> + * effect.
> >>>>>>> + *
> >>>>>>> + * Returns: 0 if successful, an negative error code otherwise
> >>>>>>> + */
> >>>>>>> +int phy_validate(struct phy *phy, enum phy_mode mode,
> >>>>>>> +		  union phy_configure_opts *opts)
> >>>>>>
> >>>>>> IIUC the consumer driver will pass configuration options (or PHY parameters)
> >>>>>> which will be validated by the PHY driver and in some cases the PHY driver can
> >>>>>> modify the configuration options? And these modified configuration options will
> >>>>>> again be given to phy_configure?
> >>>>>>
> >>>>>> Looks like it's a round about way of doing the same thing.
> >>>>>
> >>>>> Not really. The validate callback allows to check whether a particular
> >>>>> configuration would work, and try to negotiate a set of configurations
> >>>>> that both the consumer and the PHY could work with.
> >>>>
> >>>> Maybe the PHY should provide the list of supported features to the consumer
> >>>> driver and the consumer should select a supported feature?
> >>>
> >>> It's not really about the features it supports, but the boundaries it
> >>> might have on those features. For example, the same phy integrated in
> >>> two different SoCs will probably have some limit on the clock rate it
> >>> can output because of the phy design itself, but also because of the
> >>> clock that is fed into that phy, and that will be different from one
> >>> SoC to the other.
> >>>
> >>> This integration will prevent us to use some clock rates on the first
> >>> SoC, while the second one would be totally fine with it.
> >>
> >> If there's a clock that is fed to the PHY from the consumer, then the consumer
> >> driver should model a clock provider and the PHY can get a reference to it
> >> using clk_get(). Rockchip and Arasan eMMC PHYs has already used something like
> >> that.
> > 
> > That would be doable, but no current driver has had this in their
> > binding. So that would prevent any further rework, and make that whole
> > series moot. And while I could live without the Allwinner part, the
> > Cadence one is really needed.
> 
> We could add a binding and modify the driver to to register a clock provider.
> That could be included in this series itself.

That wouldn't work for device whose bindings need to remain backward
compatible. And the Allwinner part at least is in that case.

I think we should aim at making it a norm for newer bindings, but we
still have to support the old ones that cannot be changed.

> >> Assuming the PHY can get a reference to the clock provided by the consumer,
> >> what are the parameters we'll be able to get rid of in struct
> >> phy_configure_opts_mipi_dphy?
> > 
> > hs_clock_rate and lp_clock_rate. All the other ones are needed.
> 
> For a start we could use that and get rid of hs_clock_rate and lp_clock_rate in
> phy_configure_opts_mipi_dphy.

As I was saying above, I'm not sure we can do that.

> We could also use phy_set_bus_width() for lanes.

I overlooked this function somehow, it indeed looks like we can remove
the lanes part in favor of this function.

> > 
> >> I'm sorry but I'm not convinced a consumer driver should have all the details
> >> that are added in phy_configure_opts_mipi_dphy.
> > 
> > If it can convince you, here is the parameters that are needed by all
> > the MIPI-DSI drivers currently in Linux to configure their PHY:
> > 
> >   - cdns-dsi (drivers/gpu/drm/bridge/cdns-dsi.c)
> >     - hs_clk_rate
> >     - lanes
> >     - videomode
> > 
> >   - kirin (drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c)
> >     - hs_exit
> >     - hs_prepare
> >     - hs_trail
> >     - hs_zero
> >     - lpx
> >     - ta_get
> >     - ta_go
> >     - wakeup
> > 
> >   - msm (drivers/gpu/drm/msm/dsi/*)
> >     - clk_post
> >     - clk_pre
> >     - clk_prepare
> >     - clk_trail
> >     - clk_zero
> >     - hs_clk_rate
> >     - hs_exit
> >     - hs_prepare
> >     - hs_trail
> >     - hs_zero
> >     - lp_clk_rate
> >     - ta_get
> >     - ta_go
> >     - ta_sure
> > 
> >   - mtk (drivers/gpu/drm/mediatek/mtk_dsi.c)
> >     - hs_clk_rate
> >     - hs_exit
> >     - hs_prepare
> >     - hs_trail
> >     - hs_zero
> >     - lpx
> > 
> >   - sun4i (drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c)
> >     - clk_post
> >     - clk_pre
> >     - clk_prepare
> >     - clk_zero
> >     - hs_prepare
> >     - hs_trail
> >     - lanes
> >     - lp_clk_rate
> > 
> >   - tegra (drivers/gpu/drm/tegra/dsi.c)
> >     - clk_post
> >     - clk_pre
> >     - clk_prepare
> >     - clk_trail
> >     - clk_zero
> >     - hs_exit
> >     - hs_prepare
> >     - hs_trail
> >     - hs_zero
> >     - lpx
> >     - ta_get
> >     - ta_go
> >     - ta_sure
> > 
> >   - vc4 (drivers/gpu/drm/vc4/vc4_dsi.c)
> >     - hs_clk_rate
> >     - lanes
> > 
> > Now, for MIPI-CSI receivers:
> > 
> >   - marvell-ccic (drivers/media/platform/marvell-ccic/mcam-core.c)
> >     - clk_term_en
> >     - clk_settle
> >     - d_term_en
> >     - hs_settle
> >     - lp_clk_rate
> > 
> >   - omap4iss (drivers/staging/media/omap4iss/iss_csiphy.c)
> >     - clk_miss
> >     - clk_settle
> >     - clk_term
> >     - hs_settle
> >     - hs_term
> >     - lanes
> > 
> >   - rcar-vin (drivers/media/platform/rcar-vin/rcar-csi2.c)
> >     - hs_clk_rate
> >     - lanes
> > 
> >   - ti-vpe (drivers/media/platform/ti-vpe/cal.c)
> >     - clk_term_en
> >     - d_term_en
> >     - hs_settle
> >     - hs_term
> 
> Thank you for providing the exhaustive list.
>
> > So the timings expressed in the structure are the set of all the ones
> > currently used in the tree by DSI and CSI drivers. I would consider
> > that a good proof that it would be useful.
> 
> The problem I see here is each platform (PHY) will have it's own set of
> parameters and we have to keep adding members to phy_configure_opts which is
> not scalable. We should try to find a correlation between generic PHY modes and
> these parameters (at-least for a subset).

I definitely understand you skepticism towards someone coming in and
dropping such a big list of obscure parameters :)

However, those values are actually the whole list of parameters
defined by the MIPI-DPHY standard, so I really don't expect drivers to
need more than that. As you can see, most drivers allow less
parameters to be configured, but all of them are defined within those
parameters. So I'm not sure we need to worry about an ever-expanding
list of parameters: we have a limited set of parameters defined, and
from an authoritative source, so we can also push back if someone
wants to add a parameter that is implementation specific.


Maxime

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

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

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-24  9:54                 ` Maxime Ripard
@ 2018-09-24 11:55                   ` Kishon Vijay Abraham I
  2018-09-24 12:19                     ` Maxime Ripard
  0 siblings, 1 reply; 40+ messages in thread
From: Kishon Vijay Abraham I @ 2018-09-24 11:55 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: Boris Brezillon, Thomas Petazzoni, Laurent Pinchart, linux-media,
	Archit Taneja, Andrzej Hajda, Chen-Yu Tsai, linux-kernel,
	dri-devel, linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

Hi Maxime,

On Monday 24 September 2018 03:24 PM, Maxime Ripard wrote:
> Hi,
> 
> On Mon, Sep 24, 2018 at 02:18:35PM +0530, Kishon Vijay Abraham I wrote:
>> On Wednesday 19 September 2018 05:44 PM, Maxime Ripard wrote:
>>> Hi,
>>>
>>> On Fri, Sep 14, 2018 at 02:18:37PM +0530, Kishon Vijay Abraham I wrote:
>>>>>>>>> +/**
>>>>>>>>> + * phy_validate() - Checks the phy parameters
>>>>>>>>> + * @phy: the phy returned by phy_get()
>>>>>>>>> + * @mode: phy_mode the configuration is applicable to.
>>>>>>>>> + * @opts: Configuration to check
>>>>>>>>> + *
>>>>>>>>> + * Used to check that the current set of parameters can be handled by
>>>>>>>>> + * the phy. Implementations are free to tune the parameters passed as
>>>>>>>>> + * arguments if needed by some implementation detail or
>>>>>>>>> + * constraints. It will not change any actual configuration of the
>>>>>>>>> + * PHY, so calling it as many times as deemed fit will have no side
>>>>>>>>> + * effect.
>>>>>>>>> + *
>>>>>>>>> + * Returns: 0 if successful, an negative error code otherwise
>>>>>>>>> + */
>>>>>>>>> +int phy_validate(struct phy *phy, enum phy_mode mode,
>>>>>>>>> +		  union phy_configure_opts *opts)
>>>>>>>>
>>>>>>>> IIUC the consumer driver will pass configuration options (or PHY parameters)
>>>>>>>> which will be validated by the PHY driver and in some cases the PHY driver can
>>>>>>>> modify the configuration options? And these modified configuration options will
>>>>>>>> again be given to phy_configure?
>>>>>>>>
>>>>>>>> Looks like it's a round about way of doing the same thing.
>>>>>>>
>>>>>>> Not really. The validate callback allows to check whether a particular
>>>>>>> configuration would work, and try to negotiate a set of configurations
>>>>>>> that both the consumer and the PHY could work with.
>>>>>>
>>>>>> Maybe the PHY should provide the list of supported features to the consumer
>>>>>> driver and the consumer should select a supported feature?
>>>>>
>>>>> It's not really about the features it supports, but the boundaries it
>>>>> might have on those features. For example, the same phy integrated in
>>>>> two different SoCs will probably have some limit on the clock rate it
>>>>> can output because of the phy design itself, but also because of the
>>>>> clock that is fed into that phy, and that will be different from one
>>>>> SoC to the other.
>>>>>
>>>>> This integration will prevent us to use some clock rates on the first
>>>>> SoC, while the second one would be totally fine with it.
>>>>
>>>> If there's a clock that is fed to the PHY from the consumer, then the consumer
>>>> driver should model a clock provider and the PHY can get a reference to it
>>>> using clk_get(). Rockchip and Arasan eMMC PHYs has already used something like
>>>> that.
>>>
>>> That would be doable, but no current driver has had this in their
>>> binding. So that would prevent any further rework, and make that whole
>>> series moot. And while I could live without the Allwinner part, the
>>> Cadence one is really needed.
>>
>> We could add a binding and modify the driver to to register a clock provider.
>> That could be included in this series itself.
> 
> That wouldn't work for device whose bindings need to remain backward
> compatible. And the Allwinner part at least is in that case.

Er..
> 
> I think we should aim at making it a norm for newer bindings, but we
> still have to support the old ones that cannot be changed.

There are drivers which support both the old and new bindings. Allwinner could
be in that category.
> 
>>>> Assuming the PHY can get a reference to the clock provided by the consumer,
>>>> what are the parameters we'll be able to get rid of in struct
>>>> phy_configure_opts_mipi_dphy?
>>>
>>> hs_clock_rate and lp_clock_rate. All the other ones are needed.
>>
>> For a start we could use that and get rid of hs_clock_rate and lp_clock_rate in
>> phy_configure_opts_mipi_dphy.
> 
> As I was saying above, I'm not sure we can do that.
> 
>> We could also use phy_set_bus_width() for lanes.
> 
> I overlooked this function somehow, it indeed looks like we can remove
> the lanes part in favor of this function.
> 
>>>
>>>> I'm sorry but I'm not convinced a consumer driver should have all the details
>>>> that are added in phy_configure_opts_mipi_dphy.
>>>
>>> If it can convince you, here is the parameters that are needed by all
>>> the MIPI-DSI drivers currently in Linux to configure their PHY:
>>>
>>>   - cdns-dsi (drivers/gpu/drm/bridge/cdns-dsi.c)
>>>     - hs_clk_rate
>>>     - lanes
>>>     - videomode
>>>
>>>   - kirin (drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c)
>>>     - hs_exit
>>>     - hs_prepare
>>>     - hs_trail
>>>     - hs_zero
>>>     - lpx
>>>     - ta_get
>>>     - ta_go
>>>     - wakeup
>>>
>>>   - msm (drivers/gpu/drm/msm/dsi/*)
>>>     - clk_post
>>>     - clk_pre
>>>     - clk_prepare
>>>     - clk_trail
>>>     - clk_zero
>>>     - hs_clk_rate
>>>     - hs_exit
>>>     - hs_prepare
>>>     - hs_trail
>>>     - hs_zero
>>>     - lp_clk_rate
>>>     - ta_get
>>>     - ta_go
>>>     - ta_sure
>>>
>>>   - mtk (drivers/gpu/drm/mediatek/mtk_dsi.c)
>>>     - hs_clk_rate
>>>     - hs_exit
>>>     - hs_prepare
>>>     - hs_trail
>>>     - hs_zero
>>>     - lpx
>>>
>>>   - sun4i (drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c)
>>>     - clk_post
>>>     - clk_pre
>>>     - clk_prepare
>>>     - clk_zero
>>>     - hs_prepare
>>>     - hs_trail
>>>     - lanes
>>>     - lp_clk_rate
>>>
>>>   - tegra (drivers/gpu/drm/tegra/dsi.c)
>>>     - clk_post
>>>     - clk_pre
>>>     - clk_prepare
>>>     - clk_trail
>>>     - clk_zero
>>>     - hs_exit
>>>     - hs_prepare
>>>     - hs_trail
>>>     - hs_zero
>>>     - lpx
>>>     - ta_get
>>>     - ta_go
>>>     - ta_sure
>>>
>>>   - vc4 (drivers/gpu/drm/vc4/vc4_dsi.c)
>>>     - hs_clk_rate
>>>     - lanes
>>>
>>> Now, for MIPI-CSI receivers:
>>>
>>>   - marvell-ccic (drivers/media/platform/marvell-ccic/mcam-core.c)
>>>     - clk_term_en
>>>     - clk_settle
>>>     - d_term_en
>>>     - hs_settle
>>>     - lp_clk_rate
>>>
>>>   - omap4iss (drivers/staging/media/omap4iss/iss_csiphy.c)
>>>     - clk_miss
>>>     - clk_settle
>>>     - clk_term
>>>     - hs_settle
>>>     - hs_term
>>>     - lanes
>>>
>>>   - rcar-vin (drivers/media/platform/rcar-vin/rcar-csi2.c)
>>>     - hs_clk_rate
>>>     - lanes
>>>
>>>   - ti-vpe (drivers/media/platform/ti-vpe/cal.c)
>>>     - clk_term_en
>>>     - d_term_en
>>>     - hs_settle
>>>     - hs_term
>>
>> Thank you for providing the exhaustive list.
>>
>>> So the timings expressed in the structure are the set of all the ones
>>> currently used in the tree by DSI and CSI drivers. I would consider
>>> that a good proof that it would be useful.
>>
>> The problem I see here is each platform (PHY) will have it's own set of
>> parameters and we have to keep adding members to phy_configure_opts which is
>> not scalable. We should try to find a correlation between generic PHY modes and
>> these parameters (at-least for a subset).
> 
> I definitely understand you skepticism towards someone coming in and
> dropping such a big list of obscure parameters :)

That's part of my concern. The other concern is consumer driver having to know
so much internal details of the PHY. None of the other consumers (PCIe, USB,
etc) had to know so much internals of the PHY.
> 
> However, those values are actually the whole list of parameters
> defined by the MIPI-DPHY standard, so I really don't expect drivers to
> need more than that. As you can see, most drivers allow less
> parameters to be configured, but all of them are defined within those
> parameters. So I'm not sure we need to worry about an ever-expanding
> list of parameters: we have a limited set of parameters defined, and
> from an authoritative source, so we can also push back if someone
> wants to add a parameter that is implementation specific.

Your commit log mentioned there are a few parameters in addition to what is
specified in the MIPI D-PHY spec :-/

"The parameters added here are the one defined in the MIPI D-PHY spec, plus
some parameters that were used by a number of PHY drivers currently found
in the linux kernel."

Thanks
Kishon

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

* Re: [PATCH 02/10] phy: Add configuration interface
  2018-09-24 11:55                   ` Kishon Vijay Abraham I
@ 2018-09-24 12:19                     ` Maxime Ripard
  0 siblings, 0 replies; 40+ messages in thread
From: Maxime Ripard @ 2018-09-24 12:19 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: Boris Brezillon, Thomas Petazzoni, Laurent Pinchart, linux-media,
	Archit Taneja, Andrzej Hajda, Chen-Yu Tsai, linux-kernel,
	dri-devel, linux-arm-kernel, Krzysztof Witos, Rafal Ciepiela

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

Hi!

(stripping the mail a bit)

On Mon, Sep 24, 2018 at 05:25:26PM +0530, Kishon Vijay Abraham I wrote:
> >>>>> This integration will prevent us to use some clock rates on the first
> >>>>> SoC, while the second one would be totally fine with it.
> >>>>
> >>>> If there's a clock that is fed to the PHY from the consumer, then the consumer
> >>>> driver should model a clock provider and the PHY can get a reference to it
> >>>> using clk_get(). Rockchip and Arasan eMMC PHYs has already used something like
> >>>> that.
> >>>
> >>> That would be doable, but no current driver has had this in their
> >>> binding. So that would prevent any further rework, and make that whole
> >>> series moot. And while I could live without the Allwinner part, the
> >>> Cadence one is really needed.
> >>
> >> We could add a binding and modify the driver to to register a clock provider.
> >> That could be included in this series itself.
> > 
> > That wouldn't work for device whose bindings need to remain backward
> > compatible. And the Allwinner part at least is in that case.
> 
> Er..
> 
> > I think we should aim at making it a norm for newer bindings, but we
> > still have to support the old ones that cannot be changed.
> 
> There are drivers which support both the old and new bindings. Allwinner could
> be in that category.

Yes, definitely. However, in order to support the old binding, we'd
still need to have a way to give the clock rates to the phy when we
don't have that clock provider. So I don't really see how we could
remove those fields, even if we start introducing new bindings.

> >>> So the timings expressed in the structure are the set of all the ones
> >>> currently used in the tree by DSI and CSI drivers. I would consider
> >>> that a good proof that it would be useful.
> >>
> >> The problem I see here is each platform (PHY) will have it's own set of
> >> parameters and we have to keep adding members to phy_configure_opts which is
> >> not scalable. We should try to find a correlation between generic PHY modes and
> >> these parameters (at-least for a subset).
> > 
> > I definitely understand you skepticism towards someone coming in and
> > dropping such a big list of obscure parameters :)
> 
> That's part of my concern. The other concern is consumer driver having to know
> so much internal details of the PHY. None of the other consumers (PCIe, USB,
> etc) had to know so much internals of the PHY.

I guess that's true to some extent, but it also feels a bit like a
self-realizing prophecy. The phy framework also didn't provide any way
to change the phy configuration based on some runtime values up until
now, and we have a good example with the MIPI-DSI and MIPI-CSI drivers
that given the constructs used in these drivers, if the phy framework
had allowed it, they would have used it.

Also, speaking of USB, we've had some configuration that was done in
some private functions exported by the phy drivers themselves. So
there is a use case for such an interface even for other, less
configurable, phy types.

I even planned for that, since the phy_validate and phy_configure
functions are passed an union, in order to make it extensible to other
phy types easily.

I guess that both the fact that the configuration is simpler for the
phy types supported so far, and that the phy and its consumer are very
integrated, didn't make it necessary to have such an interface so
far. But with MIPI-DPHY, we have both a quite extensive configuration
to make, and the phys and their consumers seem to be a bit more
loosely integrated. HDMI phys seem to be in pretty much the same case
too.

> > However, those values are actually the whole list of parameters
> > defined by the MIPI-DPHY standard, so I really don't expect drivers to
> > need more than that. As you can see, most drivers allow less
> > parameters to be configured, but all of them are defined within those
> > parameters. So I'm not sure we need to worry about an ever-expanding
> > list of parameters: we have a limited set of parameters defined, and
> > from an authoritative source, so we can also push back if someone
> > wants to add a parameter that is implementation specific.
> 
> Your commit log mentioned there are a few parameters in addition to what is
> specified in the MIPI D-PHY spec :-/
> 
> "The parameters added here are the one defined in the MIPI D-PHY spec, plus
> some parameters that were used by a number of PHY drivers currently found
> in the linux kernel."

I should have phrased that better then, sorry. The only additions that
were made were the lanes number (that we agreed to remove), the high
speed and low power clock rates, and the video timings.

Maxime

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

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

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

end of thread, other threads:[~2018-09-24 12:19 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-05  9:16 [PATCH 00/10] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
2018-09-05  9:16 ` [PATCH 01/10] phy: Add MIPI D-PHY mode Maxime Ripard
2018-09-05 13:46   ` Laurent Pinchart
2018-09-05  9:16 ` [PATCH 02/10] phy: Add configuration interface Maxime Ripard
2018-09-05 13:39   ` Laurent Pinchart
2018-09-06 14:48     ` Maxime Ripard
2018-09-06 16:24       ` Andrew Lunn
2018-09-07  9:01         ` Maxime Ripard
2018-09-06 16:51       ` Laurent Pinchart
2018-09-07  9:07         ` Maxime Ripard
2018-09-06  9:27   ` Kishon Vijay Abraham I
2018-09-06 14:56     ` Maxime Ripard
2018-09-12  7:42       ` Kishon Vijay Abraham I
2018-09-12  8:42         ` Maxime Ripard
2018-09-14  8:48           ` Kishon Vijay Abraham I
2018-09-19 12:14             ` Maxime Ripard
2018-09-21 14:18               ` Maxime Ripard
2018-09-24  8:48               ` Kishon Vijay Abraham I
2018-09-24  9:54                 ` Maxime Ripard
2018-09-24 11:55                   ` Kishon Vijay Abraham I
2018-09-24 12:19                     ` Maxime Ripard
2018-09-05  9:16 ` [PATCH 03/10] phy: Add MIPI D-PHY configuration options Maxime Ripard
2018-09-05 13:43   ` Laurent Pinchart
2018-09-07  8:56     ` Maxime Ripard
2018-09-07 14:50       ` Laurent Pinchart
2018-09-10 14:18         ` Maxime Ripard
2018-09-05  9:16 ` [PATCH 04/10] phy: dphy: Add configuration helpers Maxime Ripard
2018-09-05 13:46   ` Laurent Pinchart
2018-09-07 13:37     ` Maxime Ripard
2018-09-07 14:26       ` Laurent Pinchart
2018-09-10 14:16         ` Maxime Ripard
2018-09-10 14:28           ` Laurent Pinchart
2018-09-05  9:16 ` [PATCH 05/10] sun6i: dsi: Convert to generic phy handling Maxime Ripard
2018-09-05  9:16 ` [PATCH 06/10] phy: Move Allwinner A31 D-PHY driver to drivers/phy/ Maxime Ripard
2018-09-05  9:16 ` [PATCH 07/10] drm/bridge: cdns: Remove mode_check test Maxime Ripard
2018-09-05  9:16 ` [PATCH 08/10] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
2018-09-05  9:16 ` [PATCH 09/10] phy: Add Cadence D-PHY support Maxime Ripard
2018-09-05 13:48   ` Laurent Pinchart
2018-09-07 13:38     ` Maxime Ripard
2018-09-05  9:16 ` [PATCH 10/10] drm/bridge: cdns: Convert to phy framework 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).