All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kishon Vijay Abraham I <kishon@ti.com>
To: Maxime Ripard <maxime.ripard@bootlin.com>,
	Boris Brezillon <boris.brezillon@bootlin.com>
Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>,
	Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
	<linux-media@vger.kernel.org>,
	Archit Taneja <architt@codeaurora.org>,
	Andrzej Hajda <a.hajda@samsung.com>, Chen-Yu Tsai <wens@csie.org>,
	<linux-kernel@vger.kernel.org>, <dri-devel@lists.freedesktop.org>,
	<linux-arm-kernel@lists.infradead.org>,
	Krzysztof Witos <kwitos@cadence.com>,
	Rafal Ciepiela <rafalc@cadence.com>
Subject: Re: [PATCH v2 8/9] phy: Add Cadence D-PHY support
Date: Tue, 20 Nov 2018 11:02:34 +0530	[thread overview]
Message-ID: <8536050b-40ab-1ec4-d325-e59bb3a92e73@ti.com> (raw)
In-Reply-To: <5f5bcc06-51b2-d565-56a0-083c66c1f01a@ti.com>

Hi,

On 12/11/18 3:21 PM, Kishon Vijay Abraham I wrote:
> Hi Maxime,
> 
> On 06/11/18 8:24 PM, 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>
>> ---
>>   Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt |  21 +-
>>   Documentation/devicetree/bindings/phy/cdns,dphy.txt           |  20 +-
>>   drivers/phy/cadence/Kconfig                                   |  13 +-
>>   drivers/phy/cadence/Makefile                                  |   1 +-
>>   drivers/phy/cadence/cdns-dphy.c                               | 459 +++++++-
>>   5 files changed, 492 insertions(+), 22 deletions(-)
>>   create mode 100644 Documentation/devicetree/bindings/phy/cdns,dphy.txt
>>   create mode 100644 drivers/phy/cadence/cdns-dphy.c
>>
>> diff --git a/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt b/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
>> index f5725bb6c61c..525a4bfd8634 100644
>> --- a/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
>> +++ b/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
>> @@ -31,28 +31,7 @@ Required subnodes:
>>   - one subnode per DSI device connected on the DSI bus. Each DSI device should
>>     contain a reg property encoding its virtual channel.
>>   
>> -Cadence DPHY
>> -============
>> -
>> -Cadence DPHY block.
>> -
>> -Required properties:
>> -- compatible: should be set to "cdns,dphy".
>> -- reg: physical base address and length of the DPHY registers.
>> -- clocks: DPHY reference clocks.
>> -- clock-names: must contain "psm" and "pll_ref".
>> -- #phy-cells: must be set to 0.
>> -
>> -
>>   Example:
>> -	dphy0: dphy@fd0e0000{
>> -		compatible = "cdns,dphy";
>> -		reg = <0x0 0xfd0e0000 0x0 0x1000>;
>> -		clocks = <&psm_clk>, <&pll_ref_clk>;
>> -		clock-names = "psm", "pll_ref";
>> -		#phy-cells = <0>;
>> -	};
>> -
>>   	dsi0: dsi@fd0c0000 {
>>   		compatible = "cdns,dsi";
>>   		reg = <0x0 0xfd0c0000 0x0 0x1000>;
>> diff --git a/Documentation/devicetree/bindings/phy/cdns,dphy.txt b/Documentation/devicetree/bindings/phy/cdns,dphy.txt
>> new file mode 100644
>> index 000000000000..1095bc4e72d9
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/cdns,dphy.txt
>> @@ -0,0 +1,20 @@
>> +Cadence DPHY
>> +============
>> +
>> +Cadence DPHY block.
>> +
>> +Required properties:
>> +- compatible: should be set to "cdns,dphy".
>> +- reg: physical base address and length of the DPHY registers.
>> +- clocks: DPHY reference clocks.
>> +- clock-names: must contain "psm" and "pll_ref".
>> +- #phy-cells: must be set to 0.
>> +
>> +Example:
>> +	dphy0: dphy@fd0e0000{
>> +		compatible = "cdns,dphy";
>> +		reg = <0x0 0xfd0e0000 0x0 0x1000>;
>> +		clocks = <&psm_clk>, <&pll_ref_clk>;
>> +		clock-names = "psm", "pll_ref";
>> +		#phy-cells = <0>;
>> +	};
>> diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
>> index 57fff7de4031..240effa81046 100644
>> --- a/drivers/phy/cadence/Kconfig
>> +++ b/drivers/phy/cadence/Kconfig
>> @@ -1,6 +1,7 @@
>>   #
>> -# Phy driver for Cadence MHDP DisplayPort controller
>> +# Phy drivers for Cadence IPs
>>   #
>> +
>>   config PHY_CADENCE_DP
>>   	tristate "Cadence MHDP DisplayPort PHY driver"
>>   	depends on OF
>> @@ -8,3 +9,13 @@ config PHY_CADENCE_DP
>>   	select GENERIC_PHY
>>   	help
>>   	  Support for Cadence MHDP DisplayPort PHY.
>> +
>> +config PHY_CADENCE_DPHY
>> +	tristate "Cadence D-PHY Support"
>> +	depends on HAS_IOMEM && OF
>> +	select GENERIC_PHY
>> +	select GENERIC_PHY_MIPI_DPHY
>> +	help
>> +	  Choose this option if you have a Cadence D-PHY in your
>> +	  system. If M is selected, the module will be called
>> +	  cdns-csi.
>> diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile
>> index e5b0a11cf28a..64cb9ea66ceb 100644
>> --- a/drivers/phy/cadence/Makefile
>> +++ b/drivers/phy/cadence/Makefile
>> @@ -1 +1,2 @@
>>   obj-$(CONFIG_PHY_CADENCE_DP)	+= phy-cadence-dp.o
>> +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..a398b401e5d3
>> --- /dev/null
>> +++ b/drivers/phy/cadence/cdns-dphy.c
>> @@ -0,0 +1,459 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Copyright: 2017-2018 Cadence Design Systems, Inc.
>> + */
>> +
>> +#include <linux/bitops.h>
>> +#include <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/reset.h>
>> +
>> +#include <linux/phy/phy.h>
>> +#include <linux/phy/phy-mipi-dphy.h>
>> +
>> +#define REG_WAKEUP_TIME_NS		800
>> +#define DPHY_PLL_RATE_HZ		108000000
>> +
>> +/* DPHY registers */
>> +#define DPHY_PMA_CMN(reg)		(reg)
>> +#define DPHY_PMA_LCLK(reg)		(0x100 + (reg))
>> +#define DPHY_PMA_LDATA(lane, reg)	(0x200 + ((lane) * 0x100) + (reg))
>> +#define DPHY_PMA_RCLK(reg)		(0x600 + (reg))
>> +#define DPHY_PMA_RDATA(lane, reg)	(0x700 + ((lane) * 0x100) + (reg))
>> +#define DPHY_PCS(reg)			(0xb00 + (reg))
>> +
>> +#define DPHY_CMN_SSM			DPHY_PMA_CMN(0x20)
>> +#define DPHY_CMN_SSM_EN			BIT(0)
>> +#define DPHY_CMN_TX_MODE_EN		BIT(9)
>> +
>> +#define DPHY_CMN_PWM			DPHY_PMA_CMN(0x40)
>> +#define DPHY_CMN_PWM_DIV(x)		((x) << 20)
>> +#define DPHY_CMN_PWM_LOW(x)		((x) << 10)
>> +#define DPHY_CMN_PWM_HIGH(x)		(x)
>> +
>> +#define DPHY_CMN_FBDIV			DPHY_PMA_CMN(0x4c)
>> +#define DPHY_CMN_FBDIV_VAL(low, high)	(((high) << 11) | ((low) << 22))
>> +#define DPHY_CMN_FBDIV_FROM_REG		(BIT(10) | BIT(21))
>> +
>> +#define DPHY_CMN_OPIPDIV		DPHY_PMA_CMN(0x50)
>> +#define DPHY_CMN_IPDIV_FROM_REG		BIT(0)
>> +#define DPHY_CMN_IPDIV(x)		((x) << 1)
>> +#define DPHY_CMN_OPDIV_FROM_REG		BIT(6)
>> +#define DPHY_CMN_OPDIV(x)		((x) << 7)
>> +
>> +#define DPHY_PSM_CFG			DPHY_PCS(0x4)
>> +#define DPHY_PSM_CFG_FROM_REG		BIT(0)
>> +#define DPHY_PSM_CLK_DIV(x)		((x) << 1)
>> +
>> +#define DSI_HBP_FRAME_OVERHEAD		12
>> +#define DSI_HSA_FRAME_OVERHEAD		14
>> +#define DSI_HFP_FRAME_OVERHEAD		6
>> +#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD	4
>> +#define DSI_BLANKING_FRAME_OVERHEAD	6
>> +#define DSI_NULL_FRAME_OVERHEAD		6
>> +#define DSI_EOT_PKT_SIZE		4
>> +
>> +struct cdns_dphy_cfg {
>> +	u8 pll_ipdiv;
>> +	u8 pll_opdiv;
>> +	u16 pll_fbdiv;
>> +	unsigned int nlanes;
>> +};
>> +
>> +enum cdns_dphy_clk_lane_cfg {
>> +	DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
>> +	DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
>> +	DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
>> +	DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
>> +};
>> +
>> +struct cdns_dphy;
>> +struct cdns_dphy_ops {
>> +	int (*probe)(struct cdns_dphy *dphy);
>> +	void (*remove)(struct cdns_dphy *dphy);
>> +	void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
>> +	void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
>> +				 enum cdns_dphy_clk_lane_cfg cfg);
>> +	void (*set_pll_cfg)(struct cdns_dphy *dphy,
>> +			    const struct cdns_dphy_cfg *cfg);
>> +	unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
>> +};
>> +
>> +struct cdns_dphy {
>> +	struct cdns_dphy_cfg cfg;
>> +	void __iomem *regs;
>> +	struct clk *psm_clk;
>> +	struct clk *pll_ref_clk;
>> +	const struct cdns_dphy_ops *ops;
>> +	struct phy *phy;
>> +};
>> +
>> +static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
>> +				     struct cdns_dphy_cfg *cfg,
>> +				     struct phy_configure_opts_mipi_dphy *opts,
>> +				     unsigned int *dsi_hfp_ext)
>> +{
>> +	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));
>> +
>> +	if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
>> +		return -EINVAL;
>> +	else if (pll_ref_hz < 19200000)
>> +		cfg->pll_ipdiv = 1;
>> +	else if (pll_ref_hz < 38400000)
>> +		cfg->pll_ipdiv = 2;
>> +	else if (pll_ref_hz < 76800000)
>> +		cfg->pll_ipdiv = 4;
>> +	else
>> +		cfg->pll_ipdiv = 8;
>> +
>> +	dlane_bps = opts->hs_clk_rate;
>> +
>> +	if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
>> +		return -EINVAL;
>> +	else if (dlane_bps >= 1250000000)
>> +		cfg->pll_opdiv = 1;
>> +	else if (dlane_bps >= 630000000)
>> +		cfg->pll_opdiv = 2;
>> +	else if (dlane_bps >= 320000000)
>> +		cfg->pll_opdiv = 4;
>> +	else if (dlane_bps >= 160000000)
>> +		cfg->pll_opdiv = 8;
>> +
>> +	/*
>> +	 * 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;
>> +
>> +		/*
>> +		 * Do the division in 2 steps to avoid an overflow on the
>> +		 * divider.
>> +		 */
>> +		rem = do_div(adj_dsi_htotal, opts->hs_clk_rate);
>> +		if (rem)
>> +			continue;
>> +
>> +		rem = do_div(adj_dsi_htotal,
>> +			     cfg->pll_opdiv * cfg->pll_ipdiv * 2 * 8);
>> +		if (rem)
>> +			continue;
>> +
>> +		cfg->pll_fbdiv = fbdiv;
>> +		break;
>> +	}
>> +
>> +	/* No match, let's just reject the display mode. */
>> +	if (!cfg->pll_fbdiv)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
>> +{
>> +	unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
>> +	unsigned long psm_div;
>> +
>> +	if (!psm_clk_hz || psm_clk_hz > 100000000)
>> +		return -EINVAL;
>> +
>> +	psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
>> +	if (dphy->ops->set_psm_div)
>> +		dphy->ops->set_psm_div(dphy, psm_div);
>> +
>> +	return 0;
>> +}
>> +
>> +static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
>> +				       enum cdns_dphy_clk_lane_cfg cfg)
>> +{
>> +	if (dphy->ops->set_clk_lane_cfg)
>> +		dphy->ops->set_clk_lane_cfg(dphy, cfg);
>> +}
>> +
>> +static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
>> +				  const struct cdns_dphy_cfg *cfg)
>> +{
>> +	if (dphy->ops->set_pll_cfg)
>> +		dphy->ops->set_pll_cfg(dphy, cfg);
>> +}
>> +
>> +static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
>> +{
>> +	return dphy->ops->get_wakeup_time_ns(dphy);
>> +}
>> +
>> +static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
>> +{
>> +	/* Default wakeup time is 800 ns (in a simulated environment). */
>> +	return 800;
>> +}
>> +
>> +static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
>> +				      const struct cdns_dphy_cfg *cfg)
>> +{
>> +	u32 fbdiv_low, fbdiv_high;
>> +
>> +	fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
>> +	fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
>> +
>> +	writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
>> +	       DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
>> +	       DPHY_CMN_OPDIV(cfg->pll_opdiv),
>> +	       dphy->regs + DPHY_CMN_OPIPDIV);
>> +	writel(DPHY_CMN_FBDIV_FROM_REG |
>> +	       DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
>> +	       dphy->regs + DPHY_CMN_FBDIV);
>> +	writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
>> +	       DPHY_CMN_PWM_DIV(0x8),
>> +	       dphy->regs + DPHY_CMN_PWM);
>> +}
>> +
>> +static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
>> +{
>> +	writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
>> +	       dphy->regs + DPHY_PSM_CFG);
>> +}
>> +
>> +/*
>> + * This is the reference implementation of DPHY hooks. Specific integration of
>> + * this IP may have to re-implement some of them depending on how they decided
>> + * to wire things in the SoC.
>> + */
>> +static const struct cdns_dphy_ops ref_dphy_ops = {
>> +	.get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
>> +	.set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
>> +	.set_psm_div = cdns_dphy_ref_set_psm_div,
>> +};
>> +
>> +static int cdns_dphy_config_from_opts(struct phy *phy,
>> +				      struct phy_configure_opts_mipi_dphy *opts,
>> +				      struct cdns_dphy_cfg *cfg)
>> +{
>> +	struct cdns_dphy *dphy = phy_get_drvdata(phy);
>> +	unsigned int dsi_hfp_ext = 0;
>> +	int ret;
>> +
>> +	ret = phy_mipi_dphy_config_validate(opts);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg,
>> +					opts, &dsi_hfp_ext);
>> +	if (ret)
>> +		return ret;
>> +
>> +	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy);

Is the wakeup populated here used by the consumer driver?

Thanks
Kishon

WARNING: multiple messages have this Message-ID (diff)
From: kishon@ti.com (Kishon Vijay Abraham I)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v2 8/9] phy: Add Cadence D-PHY support
Date: Tue, 20 Nov 2018 11:02:34 +0530	[thread overview]
Message-ID: <8536050b-40ab-1ec4-d325-e59bb3a92e73@ti.com> (raw)
In-Reply-To: <5f5bcc06-51b2-d565-56a0-083c66c1f01a@ti.com>

Hi,

On 12/11/18 3:21 PM, Kishon Vijay Abraham I wrote:
> Hi Maxime,
> 
> On 06/11/18 8:24 PM, 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>
>> ---
>>   Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt |  21 +-
>>   Documentation/devicetree/bindings/phy/cdns,dphy.txt           |  20 +-
>>   drivers/phy/cadence/Kconfig                                   |  13 +-
>>   drivers/phy/cadence/Makefile                                  |   1 +-
>>   drivers/phy/cadence/cdns-dphy.c                               | 459 +++++++-
>>   5 files changed, 492 insertions(+), 22 deletions(-)
>>   create mode 100644 Documentation/devicetree/bindings/phy/cdns,dphy.txt
>>   create mode 100644 drivers/phy/cadence/cdns-dphy.c
>>
>> diff --git a/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt b/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
>> index f5725bb6c61c..525a4bfd8634 100644
>> --- a/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
>> +++ b/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
>> @@ -31,28 +31,7 @@ Required subnodes:
>>   - one subnode per DSI device connected on the DSI bus. Each DSI device should
>>     contain a reg property encoding its virtual channel.
>>   
>> -Cadence DPHY
>> -============
>> -
>> -Cadence DPHY block.
>> -
>> -Required properties:
>> -- compatible: should be set to "cdns,dphy".
>> -- reg: physical base address and length of the DPHY registers.
>> -- clocks: DPHY reference clocks.
>> -- clock-names: must contain "psm" and "pll_ref".
>> -- #phy-cells: must be set to 0.
>> -
>> -
>>   Example:
>> -	dphy0: dphy at fd0e0000{
>> -		compatible = "cdns,dphy";
>> -		reg = <0x0 0xfd0e0000 0x0 0x1000>;
>> -		clocks = <&psm_clk>, <&pll_ref_clk>;
>> -		clock-names = "psm", "pll_ref";
>> -		#phy-cells = <0>;
>> -	};
>> -
>>   	dsi0: dsi at fd0c0000 {
>>   		compatible = "cdns,dsi";
>>   		reg = <0x0 0xfd0c0000 0x0 0x1000>;
>> diff --git a/Documentation/devicetree/bindings/phy/cdns,dphy.txt b/Documentation/devicetree/bindings/phy/cdns,dphy.txt
>> new file mode 100644
>> index 000000000000..1095bc4e72d9
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/cdns,dphy.txt
>> @@ -0,0 +1,20 @@
>> +Cadence DPHY
>> +============
>> +
>> +Cadence DPHY block.
>> +
>> +Required properties:
>> +- compatible: should be set to "cdns,dphy".
>> +- reg: physical base address and length of the DPHY registers.
>> +- clocks: DPHY reference clocks.
>> +- clock-names: must contain "psm" and "pll_ref".
>> +- #phy-cells: must be set to 0.
>> +
>> +Example:
>> +	dphy0: dphy at fd0e0000{
>> +		compatible = "cdns,dphy";
>> +		reg = <0x0 0xfd0e0000 0x0 0x1000>;
>> +		clocks = <&psm_clk>, <&pll_ref_clk>;
>> +		clock-names = "psm", "pll_ref";
>> +		#phy-cells = <0>;
>> +	};
>> diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
>> index 57fff7de4031..240effa81046 100644
>> --- a/drivers/phy/cadence/Kconfig
>> +++ b/drivers/phy/cadence/Kconfig
>> @@ -1,6 +1,7 @@
>>   #
>> -# Phy driver for Cadence MHDP DisplayPort controller
>> +# Phy drivers for Cadence IPs
>>   #
>> +
>>   config PHY_CADENCE_DP
>>   	tristate "Cadence MHDP DisplayPort PHY driver"
>>   	depends on OF
>> @@ -8,3 +9,13 @@ config PHY_CADENCE_DP
>>   	select GENERIC_PHY
>>   	help
>>   	  Support for Cadence MHDP DisplayPort PHY.
>> +
>> +config PHY_CADENCE_DPHY
>> +	tristate "Cadence D-PHY Support"
>> +	depends on HAS_IOMEM && OF
>> +	select GENERIC_PHY
>> +	select GENERIC_PHY_MIPI_DPHY
>> +	help
>> +	  Choose this option if you have a Cadence D-PHY in your
>> +	  system. If M is selected, the module will be called
>> +	  cdns-csi.
>> diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile
>> index e5b0a11cf28a..64cb9ea66ceb 100644
>> --- a/drivers/phy/cadence/Makefile
>> +++ b/drivers/phy/cadence/Makefile
>> @@ -1 +1,2 @@
>>   obj-$(CONFIG_PHY_CADENCE_DP)	+= phy-cadence-dp.o
>> +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..a398b401e5d3
>> --- /dev/null
>> +++ b/drivers/phy/cadence/cdns-dphy.c
>> @@ -0,0 +1,459 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Copyright: 2017-2018 Cadence Design Systems, Inc.
>> + */
>> +
>> +#include <linux/bitops.h>
>> +#include <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/reset.h>
>> +
>> +#include <linux/phy/phy.h>
>> +#include <linux/phy/phy-mipi-dphy.h>
>> +
>> +#define REG_WAKEUP_TIME_NS		800
>> +#define DPHY_PLL_RATE_HZ		108000000
>> +
>> +/* DPHY registers */
>> +#define DPHY_PMA_CMN(reg)		(reg)
>> +#define DPHY_PMA_LCLK(reg)		(0x100 + (reg))
>> +#define DPHY_PMA_LDATA(lane, reg)	(0x200 + ((lane) * 0x100) + (reg))
>> +#define DPHY_PMA_RCLK(reg)		(0x600 + (reg))
>> +#define DPHY_PMA_RDATA(lane, reg)	(0x700 + ((lane) * 0x100) + (reg))
>> +#define DPHY_PCS(reg)			(0xb00 + (reg))
>> +
>> +#define DPHY_CMN_SSM			DPHY_PMA_CMN(0x20)
>> +#define DPHY_CMN_SSM_EN			BIT(0)
>> +#define DPHY_CMN_TX_MODE_EN		BIT(9)
>> +
>> +#define DPHY_CMN_PWM			DPHY_PMA_CMN(0x40)
>> +#define DPHY_CMN_PWM_DIV(x)		((x) << 20)
>> +#define DPHY_CMN_PWM_LOW(x)		((x) << 10)
>> +#define DPHY_CMN_PWM_HIGH(x)		(x)
>> +
>> +#define DPHY_CMN_FBDIV			DPHY_PMA_CMN(0x4c)
>> +#define DPHY_CMN_FBDIV_VAL(low, high)	(((high) << 11) | ((low) << 22))
>> +#define DPHY_CMN_FBDIV_FROM_REG		(BIT(10) | BIT(21))
>> +
>> +#define DPHY_CMN_OPIPDIV		DPHY_PMA_CMN(0x50)
>> +#define DPHY_CMN_IPDIV_FROM_REG		BIT(0)
>> +#define DPHY_CMN_IPDIV(x)		((x) << 1)
>> +#define DPHY_CMN_OPDIV_FROM_REG		BIT(6)
>> +#define DPHY_CMN_OPDIV(x)		((x) << 7)
>> +
>> +#define DPHY_PSM_CFG			DPHY_PCS(0x4)
>> +#define DPHY_PSM_CFG_FROM_REG		BIT(0)
>> +#define DPHY_PSM_CLK_DIV(x)		((x) << 1)
>> +
>> +#define DSI_HBP_FRAME_OVERHEAD		12
>> +#define DSI_HSA_FRAME_OVERHEAD		14
>> +#define DSI_HFP_FRAME_OVERHEAD		6
>> +#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD	4
>> +#define DSI_BLANKING_FRAME_OVERHEAD	6
>> +#define DSI_NULL_FRAME_OVERHEAD		6
>> +#define DSI_EOT_PKT_SIZE		4
>> +
>> +struct cdns_dphy_cfg {
>> +	u8 pll_ipdiv;
>> +	u8 pll_opdiv;
>> +	u16 pll_fbdiv;
>> +	unsigned int nlanes;
>> +};
>> +
>> +enum cdns_dphy_clk_lane_cfg {
>> +	DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
>> +	DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
>> +	DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
>> +	DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
>> +};
>> +
>> +struct cdns_dphy;
>> +struct cdns_dphy_ops {
>> +	int (*probe)(struct cdns_dphy *dphy);
>> +	void (*remove)(struct cdns_dphy *dphy);
>> +	void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
>> +	void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
>> +				 enum cdns_dphy_clk_lane_cfg cfg);
>> +	void (*set_pll_cfg)(struct cdns_dphy *dphy,
>> +			    const struct cdns_dphy_cfg *cfg);
>> +	unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
>> +};
>> +
>> +struct cdns_dphy {
>> +	struct cdns_dphy_cfg cfg;
>> +	void __iomem *regs;
>> +	struct clk *psm_clk;
>> +	struct clk *pll_ref_clk;
>> +	const struct cdns_dphy_ops *ops;
>> +	struct phy *phy;
>> +};
>> +
>> +static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
>> +				     struct cdns_dphy_cfg *cfg,
>> +				     struct phy_configure_opts_mipi_dphy *opts,
>> +				     unsigned int *dsi_hfp_ext)
>> +{
>> +	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));
>> +
>> +	if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
>> +		return -EINVAL;
>> +	else if (pll_ref_hz < 19200000)
>> +		cfg->pll_ipdiv = 1;
>> +	else if (pll_ref_hz < 38400000)
>> +		cfg->pll_ipdiv = 2;
>> +	else if (pll_ref_hz < 76800000)
>> +		cfg->pll_ipdiv = 4;
>> +	else
>> +		cfg->pll_ipdiv = 8;
>> +
>> +	dlane_bps = opts->hs_clk_rate;
>> +
>> +	if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
>> +		return -EINVAL;
>> +	else if (dlane_bps >= 1250000000)
>> +		cfg->pll_opdiv = 1;
>> +	else if (dlane_bps >= 630000000)
>> +		cfg->pll_opdiv = 2;
>> +	else if (dlane_bps >= 320000000)
>> +		cfg->pll_opdiv = 4;
>> +	else if (dlane_bps >= 160000000)
>> +		cfg->pll_opdiv = 8;
>> +
>> +	/*
>> +	 * 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;
>> +
>> +		/*
>> +		 * Do the division in 2 steps to avoid an overflow on the
>> +		 * divider.
>> +		 */
>> +		rem = do_div(adj_dsi_htotal, opts->hs_clk_rate);
>> +		if (rem)
>> +			continue;
>> +
>> +		rem = do_div(adj_dsi_htotal,
>> +			     cfg->pll_opdiv * cfg->pll_ipdiv * 2 * 8);
>> +		if (rem)
>> +			continue;
>> +
>> +		cfg->pll_fbdiv = fbdiv;
>> +		break;
>> +	}
>> +
>> +	/* No match, let's just reject the display mode. */
>> +	if (!cfg->pll_fbdiv)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
>> +{
>> +	unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
>> +	unsigned long psm_div;
>> +
>> +	if (!psm_clk_hz || psm_clk_hz > 100000000)
>> +		return -EINVAL;
>> +
>> +	psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
>> +	if (dphy->ops->set_psm_div)
>> +		dphy->ops->set_psm_div(dphy, psm_div);
>> +
>> +	return 0;
>> +}
>> +
>> +static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
>> +				       enum cdns_dphy_clk_lane_cfg cfg)
>> +{
>> +	if (dphy->ops->set_clk_lane_cfg)
>> +		dphy->ops->set_clk_lane_cfg(dphy, cfg);
>> +}
>> +
>> +static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
>> +				  const struct cdns_dphy_cfg *cfg)
>> +{
>> +	if (dphy->ops->set_pll_cfg)
>> +		dphy->ops->set_pll_cfg(dphy, cfg);
>> +}
>> +
>> +static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
>> +{
>> +	return dphy->ops->get_wakeup_time_ns(dphy);
>> +}
>> +
>> +static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
>> +{
>> +	/* Default wakeup time is 800 ns (in a simulated environment). */
>> +	return 800;
>> +}
>> +
>> +static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
>> +				      const struct cdns_dphy_cfg *cfg)
>> +{
>> +	u32 fbdiv_low, fbdiv_high;
>> +
>> +	fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
>> +	fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
>> +
>> +	writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
>> +	       DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
>> +	       DPHY_CMN_OPDIV(cfg->pll_opdiv),
>> +	       dphy->regs + DPHY_CMN_OPIPDIV);
>> +	writel(DPHY_CMN_FBDIV_FROM_REG |
>> +	       DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
>> +	       dphy->regs + DPHY_CMN_FBDIV);
>> +	writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
>> +	       DPHY_CMN_PWM_DIV(0x8),
>> +	       dphy->regs + DPHY_CMN_PWM);
>> +}
>> +
>> +static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
>> +{
>> +	writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
>> +	       dphy->regs + DPHY_PSM_CFG);
>> +}
>> +
>> +/*
>> + * This is the reference implementation of DPHY hooks. Specific integration of
>> + * this IP may have to re-implement some of them depending on how they decided
>> + * to wire things in the SoC.
>> + */
>> +static const struct cdns_dphy_ops ref_dphy_ops = {
>> +	.get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
>> +	.set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
>> +	.set_psm_div = cdns_dphy_ref_set_psm_div,
>> +};
>> +
>> +static int cdns_dphy_config_from_opts(struct phy *phy,
>> +				      struct phy_configure_opts_mipi_dphy *opts,
>> +				      struct cdns_dphy_cfg *cfg)
>> +{
>> +	struct cdns_dphy *dphy = phy_get_drvdata(phy);
>> +	unsigned int dsi_hfp_ext = 0;
>> +	int ret;
>> +
>> +	ret = phy_mipi_dphy_config_validate(opts);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg,
>> +					opts, &dsi_hfp_ext);
>> +	if (ret)
>> +		return ret;
>> +
>> +	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy);

Is the wakeup populated here used by the consumer driver?

Thanks
Kishon

WARNING: multiple messages have this Message-ID (diff)
From: Kishon Vijay Abraham I <kishon@ti.com>
To: Maxime Ripard <maxime.ripard@bootlin.com>,
	Boris Brezillon <boris.brezillon@bootlin.com>
Cc: Thomas Petazzoni <thomas.petazzoni@bootlin.com>,
	Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
	linux-media@vger.kernel.org,
	Archit Taneja <architt@codeaurora.org>,
	Andrzej Hajda <a.hajda@samsung.com>, Chen-Yu Tsai <wens@csie.org>,
	linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org,
	linux-arm-kernel@lists.infradead.org,
	Krzysztof Witos <kwitos@cadence.com>,
	Rafal Ciepiela <rafalc@cadence.com>
Subject: Re: [PATCH v2 8/9] phy: Add Cadence D-PHY support
Date: Tue, 20 Nov 2018 11:02:34 +0530	[thread overview]
Message-ID: <8536050b-40ab-1ec4-d325-e59bb3a92e73@ti.com> (raw)
In-Reply-To: <5f5bcc06-51b2-d565-56a0-083c66c1f01a@ti.com>

Hi,

On 12/11/18 3:21 PM, Kishon Vijay Abraham I wrote:
> Hi Maxime,
> 
> On 06/11/18 8:24 PM, 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>
>> ---
>>   Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt |  21 +-
>>   Documentation/devicetree/bindings/phy/cdns,dphy.txt           |  20 +-
>>   drivers/phy/cadence/Kconfig                                   |  13 +-
>>   drivers/phy/cadence/Makefile                                  |   1 +-
>>   drivers/phy/cadence/cdns-dphy.c                               | 459 +++++++-
>>   5 files changed, 492 insertions(+), 22 deletions(-)
>>   create mode 100644 Documentation/devicetree/bindings/phy/cdns,dphy.txt
>>   create mode 100644 drivers/phy/cadence/cdns-dphy.c
>>
>> diff --git a/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt b/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
>> index f5725bb6c61c..525a4bfd8634 100644
>> --- a/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
>> +++ b/Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt
>> @@ -31,28 +31,7 @@ Required subnodes:
>>   - one subnode per DSI device connected on the DSI bus. Each DSI device should
>>     contain a reg property encoding its virtual channel.
>>   
>> -Cadence DPHY
>> -============
>> -
>> -Cadence DPHY block.
>> -
>> -Required properties:
>> -- compatible: should be set to "cdns,dphy".
>> -- reg: physical base address and length of the DPHY registers.
>> -- clocks: DPHY reference clocks.
>> -- clock-names: must contain "psm" and "pll_ref".
>> -- #phy-cells: must be set to 0.
>> -
>> -
>>   Example:
>> -	dphy0: dphy@fd0e0000{
>> -		compatible = "cdns,dphy";
>> -		reg = <0x0 0xfd0e0000 0x0 0x1000>;
>> -		clocks = <&psm_clk>, <&pll_ref_clk>;
>> -		clock-names = "psm", "pll_ref";
>> -		#phy-cells = <0>;
>> -	};
>> -
>>   	dsi0: dsi@fd0c0000 {
>>   		compatible = "cdns,dsi";
>>   		reg = <0x0 0xfd0c0000 0x0 0x1000>;
>> diff --git a/Documentation/devicetree/bindings/phy/cdns,dphy.txt b/Documentation/devicetree/bindings/phy/cdns,dphy.txt
>> new file mode 100644
>> index 000000000000..1095bc4e72d9
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/phy/cdns,dphy.txt
>> @@ -0,0 +1,20 @@
>> +Cadence DPHY
>> +============
>> +
>> +Cadence DPHY block.
>> +
>> +Required properties:
>> +- compatible: should be set to "cdns,dphy".
>> +- reg: physical base address and length of the DPHY registers.
>> +- clocks: DPHY reference clocks.
>> +- clock-names: must contain "psm" and "pll_ref".
>> +- #phy-cells: must be set to 0.
>> +
>> +Example:
>> +	dphy0: dphy@fd0e0000{
>> +		compatible = "cdns,dphy";
>> +		reg = <0x0 0xfd0e0000 0x0 0x1000>;
>> +		clocks = <&psm_clk>, <&pll_ref_clk>;
>> +		clock-names = "psm", "pll_ref";
>> +		#phy-cells = <0>;
>> +	};
>> diff --git a/drivers/phy/cadence/Kconfig b/drivers/phy/cadence/Kconfig
>> index 57fff7de4031..240effa81046 100644
>> --- a/drivers/phy/cadence/Kconfig
>> +++ b/drivers/phy/cadence/Kconfig
>> @@ -1,6 +1,7 @@
>>   #
>> -# Phy driver for Cadence MHDP DisplayPort controller
>> +# Phy drivers for Cadence IPs
>>   #
>> +
>>   config PHY_CADENCE_DP
>>   	tristate "Cadence MHDP DisplayPort PHY driver"
>>   	depends on OF
>> @@ -8,3 +9,13 @@ config PHY_CADENCE_DP
>>   	select GENERIC_PHY
>>   	help
>>   	  Support for Cadence MHDP DisplayPort PHY.
>> +
>> +config PHY_CADENCE_DPHY
>> +	tristate "Cadence D-PHY Support"
>> +	depends on HAS_IOMEM && OF
>> +	select GENERIC_PHY
>> +	select GENERIC_PHY_MIPI_DPHY
>> +	help
>> +	  Choose this option if you have a Cadence D-PHY in your
>> +	  system. If M is selected, the module will be called
>> +	  cdns-csi.
>> diff --git a/drivers/phy/cadence/Makefile b/drivers/phy/cadence/Makefile
>> index e5b0a11cf28a..64cb9ea66ceb 100644
>> --- a/drivers/phy/cadence/Makefile
>> +++ b/drivers/phy/cadence/Makefile
>> @@ -1 +1,2 @@
>>   obj-$(CONFIG_PHY_CADENCE_DP)	+= phy-cadence-dp.o
>> +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..a398b401e5d3
>> --- /dev/null
>> +++ b/drivers/phy/cadence/cdns-dphy.c
>> @@ -0,0 +1,459 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Copyright: 2017-2018 Cadence Design Systems, Inc.
>> + */
>> +
>> +#include <linux/bitops.h>
>> +#include <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/reset.h>
>> +
>> +#include <linux/phy/phy.h>
>> +#include <linux/phy/phy-mipi-dphy.h>
>> +
>> +#define REG_WAKEUP_TIME_NS		800
>> +#define DPHY_PLL_RATE_HZ		108000000
>> +
>> +/* DPHY registers */
>> +#define DPHY_PMA_CMN(reg)		(reg)
>> +#define DPHY_PMA_LCLK(reg)		(0x100 + (reg))
>> +#define DPHY_PMA_LDATA(lane, reg)	(0x200 + ((lane) * 0x100) + (reg))
>> +#define DPHY_PMA_RCLK(reg)		(0x600 + (reg))
>> +#define DPHY_PMA_RDATA(lane, reg)	(0x700 + ((lane) * 0x100) + (reg))
>> +#define DPHY_PCS(reg)			(0xb00 + (reg))
>> +
>> +#define DPHY_CMN_SSM			DPHY_PMA_CMN(0x20)
>> +#define DPHY_CMN_SSM_EN			BIT(0)
>> +#define DPHY_CMN_TX_MODE_EN		BIT(9)
>> +
>> +#define DPHY_CMN_PWM			DPHY_PMA_CMN(0x40)
>> +#define DPHY_CMN_PWM_DIV(x)		((x) << 20)
>> +#define DPHY_CMN_PWM_LOW(x)		((x) << 10)
>> +#define DPHY_CMN_PWM_HIGH(x)		(x)
>> +
>> +#define DPHY_CMN_FBDIV			DPHY_PMA_CMN(0x4c)
>> +#define DPHY_CMN_FBDIV_VAL(low, high)	(((high) << 11) | ((low) << 22))
>> +#define DPHY_CMN_FBDIV_FROM_REG		(BIT(10) | BIT(21))
>> +
>> +#define DPHY_CMN_OPIPDIV		DPHY_PMA_CMN(0x50)
>> +#define DPHY_CMN_IPDIV_FROM_REG		BIT(0)
>> +#define DPHY_CMN_IPDIV(x)		((x) << 1)
>> +#define DPHY_CMN_OPDIV_FROM_REG		BIT(6)
>> +#define DPHY_CMN_OPDIV(x)		((x) << 7)
>> +
>> +#define DPHY_PSM_CFG			DPHY_PCS(0x4)
>> +#define DPHY_PSM_CFG_FROM_REG		BIT(0)
>> +#define DPHY_PSM_CLK_DIV(x)		((x) << 1)
>> +
>> +#define DSI_HBP_FRAME_OVERHEAD		12
>> +#define DSI_HSA_FRAME_OVERHEAD		14
>> +#define DSI_HFP_FRAME_OVERHEAD		6
>> +#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD	4
>> +#define DSI_BLANKING_FRAME_OVERHEAD	6
>> +#define DSI_NULL_FRAME_OVERHEAD		6
>> +#define DSI_EOT_PKT_SIZE		4
>> +
>> +struct cdns_dphy_cfg {
>> +	u8 pll_ipdiv;
>> +	u8 pll_opdiv;
>> +	u16 pll_fbdiv;
>> +	unsigned int nlanes;
>> +};
>> +
>> +enum cdns_dphy_clk_lane_cfg {
>> +	DPHY_CLK_CFG_LEFT_DRIVES_ALL = 0,
>> +	DPHY_CLK_CFG_LEFT_DRIVES_RIGHT = 1,
>> +	DPHY_CLK_CFG_LEFT_DRIVES_LEFT = 2,
>> +	DPHY_CLK_CFG_RIGHT_DRIVES_ALL = 3,
>> +};
>> +
>> +struct cdns_dphy;
>> +struct cdns_dphy_ops {
>> +	int (*probe)(struct cdns_dphy *dphy);
>> +	void (*remove)(struct cdns_dphy *dphy);
>> +	void (*set_psm_div)(struct cdns_dphy *dphy, u8 div);
>> +	void (*set_clk_lane_cfg)(struct cdns_dphy *dphy,
>> +				 enum cdns_dphy_clk_lane_cfg cfg);
>> +	void (*set_pll_cfg)(struct cdns_dphy *dphy,
>> +			    const struct cdns_dphy_cfg *cfg);
>> +	unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy);
>> +};
>> +
>> +struct cdns_dphy {
>> +	struct cdns_dphy_cfg cfg;
>> +	void __iomem *regs;
>> +	struct clk *psm_clk;
>> +	struct clk *pll_ref_clk;
>> +	const struct cdns_dphy_ops *ops;
>> +	struct phy *phy;
>> +};
>> +
>> +static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy,
>> +				     struct cdns_dphy_cfg *cfg,
>> +				     struct phy_configure_opts_mipi_dphy *opts,
>> +				     unsigned int *dsi_hfp_ext)
>> +{
>> +	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));
>> +
>> +	if (pll_ref_hz < 9600000 || pll_ref_hz >= 150000000)
>> +		return -EINVAL;
>> +	else if (pll_ref_hz < 19200000)
>> +		cfg->pll_ipdiv = 1;
>> +	else if (pll_ref_hz < 38400000)
>> +		cfg->pll_ipdiv = 2;
>> +	else if (pll_ref_hz < 76800000)
>> +		cfg->pll_ipdiv = 4;
>> +	else
>> +		cfg->pll_ipdiv = 8;
>> +
>> +	dlane_bps = opts->hs_clk_rate;
>> +
>> +	if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL)
>> +		return -EINVAL;
>> +	else if (dlane_bps >= 1250000000)
>> +		cfg->pll_opdiv = 1;
>> +	else if (dlane_bps >= 630000000)
>> +		cfg->pll_opdiv = 2;
>> +	else if (dlane_bps >= 320000000)
>> +		cfg->pll_opdiv = 4;
>> +	else if (dlane_bps >= 160000000)
>> +		cfg->pll_opdiv = 8;
>> +
>> +	/*
>> +	 * 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;
>> +
>> +		/*
>> +		 * Do the division in 2 steps to avoid an overflow on the
>> +		 * divider.
>> +		 */
>> +		rem = do_div(adj_dsi_htotal, opts->hs_clk_rate);
>> +		if (rem)
>> +			continue;
>> +
>> +		rem = do_div(adj_dsi_htotal,
>> +			     cfg->pll_opdiv * cfg->pll_ipdiv * 2 * 8);
>> +		if (rem)
>> +			continue;
>> +
>> +		cfg->pll_fbdiv = fbdiv;
>> +		break;
>> +	}
>> +
>> +	/* No match, let's just reject the display mode. */
>> +	if (!cfg->pll_fbdiv)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>> +static int cdns_dphy_setup_psm(struct cdns_dphy *dphy)
>> +{
>> +	unsigned long psm_clk_hz = clk_get_rate(dphy->psm_clk);
>> +	unsigned long psm_div;
>> +
>> +	if (!psm_clk_hz || psm_clk_hz > 100000000)
>> +		return -EINVAL;
>> +
>> +	psm_div = DIV_ROUND_CLOSEST(psm_clk_hz, 1000000);
>> +	if (dphy->ops->set_psm_div)
>> +		dphy->ops->set_psm_div(dphy, psm_div);
>> +
>> +	return 0;
>> +}
>> +
>> +static void cdns_dphy_set_clk_lane_cfg(struct cdns_dphy *dphy,
>> +				       enum cdns_dphy_clk_lane_cfg cfg)
>> +{
>> +	if (dphy->ops->set_clk_lane_cfg)
>> +		dphy->ops->set_clk_lane_cfg(dphy, cfg);
>> +}
>> +
>> +static void cdns_dphy_set_pll_cfg(struct cdns_dphy *dphy,
>> +				  const struct cdns_dphy_cfg *cfg)
>> +{
>> +	if (dphy->ops->set_pll_cfg)
>> +		dphy->ops->set_pll_cfg(dphy, cfg);
>> +}
>> +
>> +static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy)
>> +{
>> +	return dphy->ops->get_wakeup_time_ns(dphy);
>> +}
>> +
>> +static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy)
>> +{
>> +	/* Default wakeup time is 800 ns (in a simulated environment). */
>> +	return 800;
>> +}
>> +
>> +static void cdns_dphy_ref_set_pll_cfg(struct cdns_dphy *dphy,
>> +				      const struct cdns_dphy_cfg *cfg)
>> +{
>> +	u32 fbdiv_low, fbdiv_high;
>> +
>> +	fbdiv_low = (cfg->pll_fbdiv / 4) - 2;
>> +	fbdiv_high = cfg->pll_fbdiv - fbdiv_low - 2;
>> +
>> +	writel(DPHY_CMN_IPDIV_FROM_REG | DPHY_CMN_OPDIV_FROM_REG |
>> +	       DPHY_CMN_IPDIV(cfg->pll_ipdiv) |
>> +	       DPHY_CMN_OPDIV(cfg->pll_opdiv),
>> +	       dphy->regs + DPHY_CMN_OPIPDIV);
>> +	writel(DPHY_CMN_FBDIV_FROM_REG |
>> +	       DPHY_CMN_FBDIV_VAL(fbdiv_low, fbdiv_high),
>> +	       dphy->regs + DPHY_CMN_FBDIV);
>> +	writel(DPHY_CMN_PWM_HIGH(6) | DPHY_CMN_PWM_LOW(0x101) |
>> +	       DPHY_CMN_PWM_DIV(0x8),
>> +	       dphy->regs + DPHY_CMN_PWM);
>> +}
>> +
>> +static void cdns_dphy_ref_set_psm_div(struct cdns_dphy *dphy, u8 div)
>> +{
>> +	writel(DPHY_PSM_CFG_FROM_REG | DPHY_PSM_CLK_DIV(div),
>> +	       dphy->regs + DPHY_PSM_CFG);
>> +}
>> +
>> +/*
>> + * This is the reference implementation of DPHY hooks. Specific integration of
>> + * this IP may have to re-implement some of them depending on how they decided
>> + * to wire things in the SoC.
>> + */
>> +static const struct cdns_dphy_ops ref_dphy_ops = {
>> +	.get_wakeup_time_ns = cdns_dphy_ref_get_wakeup_time_ns,
>> +	.set_pll_cfg = cdns_dphy_ref_set_pll_cfg,
>> +	.set_psm_div = cdns_dphy_ref_set_psm_div,
>> +};
>> +
>> +static int cdns_dphy_config_from_opts(struct phy *phy,
>> +				      struct phy_configure_opts_mipi_dphy *opts,
>> +				      struct cdns_dphy_cfg *cfg)
>> +{
>> +	struct cdns_dphy *dphy = phy_get_drvdata(phy);
>> +	unsigned int dsi_hfp_ext = 0;
>> +	int ret;
>> +
>> +	ret = phy_mipi_dphy_config_validate(opts);
>> +	if (ret)
>> +		return ret;
>> +
>> +	ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg,
>> +					opts, &dsi_hfp_ext);
>> +	if (ret)
>> +		return ret;
>> +
>> +	opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy);

Is the wakeup populated here used by the consumer driver?

Thanks
Kishon

  parent reply	other threads:[~2018-11-20  5:33 UTC|newest]

Thread overview: 83+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-11-06 14:54 [PATCH v2 0/9] phy: Add configuration interface for MIPI D-PHY devices Maxime Ripard
2018-11-06 14:54 ` Maxime Ripard
2018-11-06 14:54 ` Maxime Ripard
2018-11-06 14:54 ` [PATCH v2 1/9] phy: Add MIPI D-PHY mode Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-11-07 13:29   ` Sakari Ailus
2018-11-07 13:29     ` Sakari Ailus
2018-11-06 14:54 ` [PATCH v2 2/9] phy: Add configuration interface Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-11-12 10:02   ` Kishon Vijay Abraham I
2018-11-12 10:02     ` Kishon Vijay Abraham I
2018-11-12 10:02     ` Kishon Vijay Abraham I
2018-11-14 10:51     ` Maxime Ripard
2018-11-14 10:51       ` Maxime Ripard
2018-11-14 10:51       ` Maxime Ripard
2018-11-06 14:54 ` [PATCH v2 3/9] phy: Add MIPI D-PHY configuration options Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-11-19 13:58   ` Sakari Ailus
2018-11-19 13:58     ` Sakari Ailus
2018-11-19 13:58     ` Sakari Ailus
2018-11-21  9:52     ` Maxime Ripard
2018-11-21  9:52       ` Maxime Ripard
2018-11-21  9:52       ` Maxime Ripard
2018-11-06 14:54 ` [PATCH v2 4/9] phy: dphy: Add configuration helpers Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-11-19 13:43   ` Sakari Ailus
2018-11-19 13:43     ` Sakari Ailus
2018-11-19 13:43     ` Sakari Ailus
2018-11-21  9:33     ` Maxime Ripard
2018-11-21  9:33       ` Maxime Ripard
2018-12-04  5:58       ` Kishon Vijay Abraham I
2018-12-04  5:58         ` Kishon Vijay Abraham I
2018-12-04  5:58         ` Kishon Vijay Abraham I
2018-12-04 15:48         ` Maxime Ripard
2018-12-04 15:48           ` Maxime Ripard
2018-12-04 15:48           ` Maxime Ripard
2018-11-06 14:54 ` [PATCH v2 5/9] sun6i: dsi: Convert to generic phy handling Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-11-06 14:54 ` [PATCH v2 6/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/ Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-11-19 17:05   ` Sakari Ailus
2018-11-19 17:05     ` Sakari Ailus
2018-11-19 17:05     ` Sakari Ailus
2018-11-06 14:54 ` [PATCH v2 7/9] drm/bridge: cdns: Separate DSI and D-PHY configuration Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-11-06 14:54 ` [PATCH v2 8/9] phy: Add Cadence D-PHY support Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-11-12  9:51   ` Kishon Vijay Abraham I
2018-11-12  9:51     ` Kishon Vijay Abraham I
2018-11-12  9:51     ` Kishon Vijay Abraham I
2018-11-16  9:17     ` Maxime Ripard
2018-11-16  9:17       ` Maxime Ripard
2018-11-20  5:32     ` Kishon Vijay Abraham I [this message]
2018-11-20  5:32       ` Kishon Vijay Abraham I
2018-11-20  5:32       ` Kishon Vijay Abraham I
2018-11-21 10:11       ` Maxime Ripard
2018-11-21 10:11         ` Maxime Ripard
2018-11-21 10:11         ` Maxime Ripard
2018-11-21 10:29         ` Kishon Vijay Abraham I
2018-11-21 10:29           ` Kishon Vijay Abraham I
2018-11-21 10:29           ` Kishon Vijay Abraham I
2018-11-21 13:47           ` Maxime Ripard
2018-11-21 13:47             ` Maxime Ripard
2018-11-21 13:47             ` Maxime Ripard
2018-11-22  8:46             ` Kishon Vijay Abraham I
2018-11-22  8:46               ` Kishon Vijay Abraham I
2018-11-22  8:46               ` Kishon Vijay Abraham I
2018-11-19 21:24   ` Sakari Ailus
2018-11-19 21:24     ` Sakari Ailus
2018-11-21 13:21     ` Maxime Ripard
2018-11-21 13:21       ` Maxime Ripard
2018-11-06 14:54 ` [PATCH v2 9/9] drm/bridge: cdns: Convert to phy framework Maxime Ripard
2018-11-06 14:54   ` Maxime Ripard
2018-12-07  5:00 ` [PATCH v2 0/9] phy: Add configuration interface for MIPI D-PHY devices Kishon Vijay Abraham I
2018-12-07  5:00   ` Kishon Vijay Abraham I
2018-12-07  5:00   ` Kishon Vijay Abraham I
2018-12-07  9:09   ` Maxime Ripard
2018-12-07  9:09     ` Maxime Ripard
2018-12-07  9:09     ` Maxime Ripard

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8536050b-40ab-1ec4-d325-e59bb3a92e73@ti.com \
    --to=kishon@ti.com \
    --cc=a.hajda@samsung.com \
    --cc=architt@codeaurora.org \
    --cc=boris.brezillon@bootlin.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=kwitos@cadence.com \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=maxime.ripard@bootlin.com \
    --cc=rafalc@cadence.com \
    --cc=thomas.petazzoni@bootlin.com \
    --cc=wens@csie.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.