2019-01-21 15:47:55

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices

Hi,

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

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

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

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

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

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

Let me know what you think,
Maxime

Changes from v4:
- Removed regression on the variable calculation
- Fixed the wakeup unit
- Collected Sean Acked-by on the last patch
- Collected Sakari Reviewed-by on the first patch

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

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

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

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

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

base-commit: bfeffd155283772bbe78c6a05dec7c0128ee500c
--
git-series 0.9.1


2019-01-21 15:48:02

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v5 2/9] phy: dphy: Change units of wakeup and init parameters

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

Move them to microseconds which should be better fit.

Suggested-by: Sakari Ailus <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
Signed-off-by: Maxime Ripard <[email protected]>
---
drivers/phy/phy-core-mipi-dphy.c | 8 ++++----
include/linux/phy/phy-mipi-dphy.h | 8 ++++----
2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/phy/phy-core-mipi-dphy.c b/drivers/phy/phy-core-mipi-dphy.c
index 465fa1b91a5f..14e0551cd319 100644
--- a/drivers/phy/phy-core-mipi-dphy.c
+++ b/drivers/phy/phy-core-mipi-dphy.c
@@ -65,12 +65,12 @@ int phy_mipi_dphy_get_default_config(unsigned long pixel_clock,
*/
cfg->hs_trail = max(4 * 8 * ui, 60000 + 4 * 4 * ui);

- cfg->init = 100000000;
+ cfg->init = 100;
cfg->lpx = 60000;
cfg->ta_get = 5 * cfg->lpx;
cfg->ta_go = 4 * cfg->lpx;
cfg->ta_sure = 2 * cfg->lpx;
- cfg->wakeup = 1000000000;
+ cfg->wakeup = 1000;

cfg->hs_clk_rate = hs_clk_rate;
cfg->lanes = lanes;
@@ -143,7 +143,7 @@ int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
if (cfg->hs_trail < max(8 * ui, 60000 + 4 * ui))
return -EINVAL;

- if (cfg->init < 100000000)
+ if (cfg->init < 100)
return -EINVAL;

if (cfg->lpx < 50000)
@@ -158,7 +158,7 @@ int phy_mipi_dphy_config_validate(struct phy_configure_opts_mipi_dphy *cfg)
if (cfg->ta_sure < cfg->lpx || cfg->ta_sure > (2 * cfg->lpx))
return -EINVAL;

- if (cfg->wakeup < 1000000000)
+ if (cfg->wakeup < 1000)
return -EINVAL;

return 0;
diff --git a/include/linux/phy/phy-mipi-dphy.h b/include/linux/phy/phy-mipi-dphy.h
index 9cf97cd1d303..627d28080d3a 100644
--- a/include/linux/phy/phy-mipi-dphy.h
+++ b/include/linux/phy/phy-mipi-dphy.h
@@ -190,10 +190,10 @@ struct phy_configure_opts_mipi_dphy {
/**
* @init:
*
- * Time, in picoseconds for the initialization period to
+ * Time, in microseconds for the initialization period to
* complete.
*
- * Minimum value: 100000000 ps
+ * Minimum value: 100 us
*/
unsigned int init;

@@ -244,11 +244,11 @@ struct phy_configure_opts_mipi_dphy {
/**
* @wakeup:
*
- * Time, in picoseconds, that a transmitter drives a Mark-1
+ * Time, in microseconds, that a transmitter drives a Mark-1
* state prior to a Stop state in order to initiate an exit
* from ULPS.
*
- * Minimum value: 1000000000 ps
+ * Minimum value: 1000 us
*/
unsigned int wakeup;

--
git-series 0.9.1

2019-01-21 15:48:12

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v5 5/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/

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 <[email protected]>
---
drivers/gpu/drm/sun4i/Kconfig | 10 +-
drivers/gpu/drm/sun4i/Makefile | 1 +-
drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c | 318 +---------------------
drivers/phy/allwinner/Kconfig | 12 +-
drivers/phy/allwinner/Makefile | 1 +-
drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 318 +++++++++++++++++++++-
6 files changed, 332 insertions(+), 328 deletions(-)
delete mode 100644 drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
create mode 100644 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index 2b8db82c4bab..1dbbc3a1b763 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -45,20 +45,12 @@ config DRM_SUN6I_DSI
default MACH_SUN8I
select CRC_CCITT
select DRM_MIPI_DSI
- select DRM_SUN6I_DPHY
+ select PHY_SUN6I_MIPI_DPHY
help
Choose this option if you want have an Allwinner SoC with
MIPI-DSI support. If M is selected the module will be called
sun6i_mipi_dsi.

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

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

2019-01-21 15:49:10

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v5 4/9] sun6i: dsi: Convert to generic phy handling

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 <[email protected]>
---
drivers/gpu/drm/sun4i/Kconfig | 11 +-
drivers/gpu/drm/sun4i/Makefile | 6 +-
drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c | 164 ++++++++++++++-----------
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 31 ++---
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h | 17 +---
5 files changed, 126 insertions(+), 103 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
index c2c042287c19..2b8db82c4bab 100644
--- a/drivers/gpu/drm/sun4i/Kconfig
+++ b/drivers/gpu/drm/sun4i/Kconfig
@@ -45,10 +45,19 @@ config DRM_SUN6I_DSI
default MACH_SUN8I
select CRC_CCITT
select DRM_MIPI_DSI
+ select DRM_SUN6I_DPHY
help
Choose this option if you want have an Allwinner SoC with
MIPI-DSI support. If M is selected the module will be called
- sun6i-dsi
+ sun6i_mipi_dsi.
+
+config DRM_SUN6I_DPHY
+ tristate "Allwinner A31 MIPI D-PHY Support"
+ select GENERIC_PHY_MIPI_DPHY
+ help
+ Choose this option if you have an Allwinner SoC with
+ MIPI-DSI support. If M is selected, the module will be
+ called sun6i_mipi_dphy.

config DRM_SUN8I_DW_HDMI
tristate "Support for Allwinner version of DesignWare HDMI"
diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
index 0eb38ac8e86e..1e2320d824b5 100644
--- a/drivers/gpu/drm/sun4i/Makefile
+++ b/drivers/gpu/drm/sun4i/Makefile
@@ -24,9 +24,6 @@ sun4i-tcon-y += sun4i_lvds.o
sun4i-tcon-y += sun4i_tcon.o
sun4i-tcon-y += sun4i_rgb.o

-sun6i-dsi-y += sun6i_mipi_dphy.o
-sun6i-dsi-y += sun6i_mipi_dsi.o
-
obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o
obj-$(CONFIG_DRM_SUN4I) += sun4i-tcon.o
obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
@@ -37,7 +34,8 @@ ifdef CONFIG_DRM_SUN4I_BACKEND
obj-$(CONFIG_DRM_SUN4I) += sun4i-frontend.o
endif
obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o
-obj-$(CONFIG_DRM_SUN6I_DSI) += sun6i-dsi.o
+obj-$(CONFIG_DRM_SUN6I_DPHY) += sun6i_mipi_dphy.o
+obj-$(CONFIG_DRM_SUN6I_DSI) += sun6i_mipi_dsi.o
obj-$(CONFIG_DRM_SUN8I_DW_HDMI) += sun8i-drm-hdmi.o
obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o
obj-$(CONFIG_DRM_SUN8I_TCON_TOP) += sun8i_tcon_top.o
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
index e4d19431fa0e..79c8af5c7c1d 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
@@ -8,11 +8,14 @@

#include <linux/bitops.h>
#include <linux/clk.h>
+#include <linux/module.h>
#include <linux/of_address.h>
+#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>

-#include "sun6i_mipi_dsi.h"
+#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>

#define SUN6I_DPHY_GCTL_REG 0x00
#define SUN6I_DPHY_GCTL_LANE_NUM(n) ((((n) - 1) & 3) << 4)
@@ -81,12 +84,46 @@

#define SUN6I_DPHY_DBG5_REG 0xf4

-int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes)
+struct sun6i_dphy {
+ struct clk *bus_clk;
+ struct clk *mod_clk;
+ struct regmap *regs;
+ struct reset_control *reset;
+
+ struct phy *phy;
+ struct phy_configure_opts_mipi_dphy config;
+};
+
+static int sun6i_dphy_init(struct phy *phy)
{
+ struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+
reset_control_deassert(dphy->reset);
clk_prepare_enable(dphy->mod_clk);
clk_set_rate_exclusive(dphy->mod_clk, 150000000);

+ return 0;
+}
+
+static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
+{
+ struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+ int ret;
+
+ ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
+ if (ret)
+ return ret;
+
+ memcpy(&dphy->config, opts, sizeof(dphy->config));
+
+ return 0;
+}
+
+static int sun6i_dphy_power_on(struct phy *phy)
+{
+ struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+ u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
+
regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);

@@ -111,16 +148,9 @@ int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes)
SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));

regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
- SUN6I_DPHY_GCTL_LANE_NUM(lanes) |
+ SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
SUN6I_DPHY_GCTL_EN);

- return 0;
-}
-
-int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes)
-{
- u8 lanes_mask = GENMASK(lanes - 1, 0);
-
regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
SUN6I_DPHY_ANA0_REG_PWS |
SUN6I_DPHY_ANA0_REG_DMPC |
@@ -181,16 +211,20 @@ int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes)
return 0;
}

-int sun6i_dphy_power_off(struct sun6i_dphy *dphy)
+static int sun6i_dphy_power_off(struct phy *phy)
{
+ struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+
regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
SUN6I_DPHY_ANA1_REG_VTTMODE, 0);

return 0;
}

-int sun6i_dphy_exit(struct sun6i_dphy *dphy)
+static int sun6i_dphy_exit(struct phy *phy)
{
+ struct sun6i_dphy *dphy = phy_get_drvdata(phy);
+
clk_rate_exclusive_put(dphy->mod_clk);
clk_disable_unprepare(dphy->mod_clk);
reset_control_assert(dphy->reset);
@@ -198,6 +232,15 @@ int sun6i_dphy_exit(struct sun6i_dphy *dphy)
return 0;
}

+
+static struct phy_ops sun6i_dphy_ops = {
+ .configure = sun6i_dphy_configure,
+ .power_on = sun6i_dphy_power_on,
+ .power_off = sun6i_dphy_power_off,
+ .init = sun6i_dphy_init,
+ .exit = sun6i_dphy_exit,
+};
+
static struct regmap_config sun6i_dphy_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
@@ -206,87 +249,70 @@ static struct regmap_config sun6i_dphy_regmap_config = {
.name = "mipi-dphy",
};

-static const struct of_device_id sun6i_dphy_of_table[] = {
- { .compatible = "allwinner,sun6i-a31-mipi-dphy" },
- { }
-};
-
-int sun6i_dphy_probe(struct sun6i_dsi *dsi, struct device_node *node)
+static int sun6i_dphy_probe(struct platform_device *pdev)
{
+ struct phy_provider *phy_provider;
struct sun6i_dphy *dphy;
- struct resource res;
+ struct resource *res;
void __iomem *regs;
- int ret;
-
- if (!of_match_node(sun6i_dphy_of_table, node)) {
- dev_err(dsi->dev, "Incompatible D-PHY\n");
- return -EINVAL;
- }

- dphy = devm_kzalloc(dsi->dev, sizeof(*dphy), GFP_KERNEL);
+ dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
if (!dphy)
return -ENOMEM;

- ret = of_address_to_resource(node, 0, &res);
- if (ret) {
- dev_err(dsi->dev, "phy: Couldn't get our resources\n");
- return ret;
- }
-
- regs = devm_ioremap_resource(dsi->dev, &res);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(regs)) {
- dev_err(dsi->dev, "Couldn't map the DPHY encoder registers\n");
+ dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n");
return PTR_ERR(regs);
}

- dphy->regs = devm_regmap_init_mmio(dsi->dev, regs,
- &sun6i_dphy_regmap_config);
+ dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus",
+ regs, &sun6i_dphy_regmap_config);
if (IS_ERR(dphy->regs)) {
- dev_err(dsi->dev, "Couldn't create the DPHY encoder regmap\n");
+ dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n");
return PTR_ERR(dphy->regs);
}

- dphy->reset = of_reset_control_get_shared(node, NULL);
+ dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
if (IS_ERR(dphy->reset)) {
- dev_err(dsi->dev, "Couldn't get our reset line\n");
+ dev_err(&pdev->dev, "Couldn't get our reset line\n");
return PTR_ERR(dphy->reset);
}

- dphy->bus_clk = of_clk_get_by_name(node, "bus");
- if (IS_ERR(dphy->bus_clk)) {
- dev_err(dsi->dev, "Couldn't get the DPHY bus clock\n");
- ret = PTR_ERR(dphy->bus_clk);
- goto err_free_reset;
- }
- regmap_mmio_attach_clk(dphy->regs, dphy->bus_clk);
-
- dphy->mod_clk = of_clk_get_by_name(node, "mod");
+ dphy->mod_clk = devm_clk_get(&pdev->dev, "mod");
if (IS_ERR(dphy->mod_clk)) {
- dev_err(dsi->dev, "Couldn't get the DPHY mod clock\n");
- ret = PTR_ERR(dphy->mod_clk);
- goto err_free_bus;
+ dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n");
+ return PTR_ERR(dphy->mod_clk);
}

- dsi->dphy = dphy;
+ dphy->phy = devm_phy_create(&pdev->dev, NULL, &sun6i_dphy_ops);
+ if (IS_ERR(dphy->phy)) {
+ dev_err(&pdev->dev, "failed to create PHY\n");
+ return PTR_ERR(dphy->phy);
+ }

- return 0;
+ phy_set_drvdata(dphy->phy, dphy);
+ phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);

-err_free_bus:
- regmap_mmio_detach_clk(dphy->regs);
- clk_put(dphy->bus_clk);
-err_free_reset:
- reset_control_put(dphy->reset);
- return ret;
+ return PTR_ERR_OR_ZERO(phy_provider);
}

-int sun6i_dphy_remove(struct sun6i_dsi *dsi)
-{
- struct sun6i_dphy *dphy = dsi->dphy;
-
- regmap_mmio_detach_clk(dphy->regs);
- clk_put(dphy->mod_clk);
- clk_put(dphy->bus_clk);
- reset_control_put(dphy->reset);
+static const struct of_device_id sun6i_dphy_of_table[] = {
+ { .compatible = "allwinner,sun6i-a31-mipi-dphy" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table);
+
+static struct platform_driver sun6i_dphy_platform_driver = {
+ .probe = sun6i_dphy_probe,
+ .driver = {
+ .name = "sun6i-mipi-dphy",
+ .of_match_table = sun6i_dphy_of_table,
+ },
+};
+module_platform_driver(sun6i_dphy_platform_driver);

- return 0;
-}
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>");
+MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
index e3b34a345546..7bbce7708265 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
@@ -16,6 +16,7 @@
#include <linux/slab.h>

#include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>

#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
@@ -616,6 +617,8 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
struct sun6i_dsi *dsi = encoder_to_sun6i_dsi(encoder);
struct mipi_dsi_device *device = dsi->device;
+ union phy_configure_opts opts = { 0 };
+ struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
u16 delay;

DRM_DEBUG_DRIVER("Enabling DSI output\n");
@@ -634,8 +637,15 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
sun6i_dsi_setup_format(dsi, mode);
sun6i_dsi_setup_timings(dsi, mode);

- sun6i_dphy_init(dsi->dphy, device->lanes);
- sun6i_dphy_power_on(dsi->dphy, device->lanes);
+ phy_init(dsi->dphy);
+
+ phy_mipi_dphy_get_default_config(mode->clock * 1000,
+ mipi_dsi_pixel_format_to_bpp(device->format),
+ device->lanes, cfg);
+
+ phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY);
+ phy_configure(dsi->dphy, &opts);
+ phy_power_on(dsi->dphy);

if (!IS_ERR(dsi->panel))
drm_panel_prepare(dsi->panel);
@@ -673,8 +683,8 @@ static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder)
drm_panel_unprepare(dsi->panel);
}

- sun6i_dphy_power_off(dsi->dphy);
- sun6i_dphy_exit(dsi->dphy);
+ phy_power_off(dsi->dphy);
+ phy_exit(dsi->dphy);

pm_runtime_put(dsi->dev);
}
@@ -967,7 +977,6 @@ static const struct component_ops sun6i_dsi_ops = {
static int sun6i_dsi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *dphy_node;
struct sun6i_dsi *dsi;
struct resource *res;
void __iomem *base;
@@ -1013,10 +1022,8 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
*/
clk_set_rate_exclusive(dsi->mod_clk, 297000000);

- dphy_node = of_parse_phandle(dev->of_node, "phys", 0);
- ret = sun6i_dphy_probe(dsi, dphy_node);
- of_node_put(dphy_node);
- if (ret) {
+ dsi->dphy = devm_phy_get(dev, "dphy");
+ if (IS_ERR(dsi->dphy)) {
dev_err(dev, "Couldn't get the MIPI D-PHY\n");
goto err_unprotect_clk;
}
@@ -1026,7 +1033,7 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
ret = mipi_dsi_host_register(&dsi->host);
if (ret) {
dev_err(dev, "Couldn't register MIPI-DSI host\n");
- goto err_remove_phy;
+ goto err_pm_disable;
}

ret = component_add(&pdev->dev, &sun6i_dsi_ops);
@@ -1039,9 +1046,8 @@ static int sun6i_dsi_probe(struct platform_device *pdev)

err_remove_dsi_host:
mipi_dsi_host_unregister(&dsi->host);
-err_remove_phy:
+err_pm_disable:
pm_runtime_disable(dev);
- sun6i_dphy_remove(dsi);
err_unprotect_clk:
clk_rate_exclusive_put(dsi->mod_clk);
return ret;
@@ -1055,7 +1061,6 @@ static int sun6i_dsi_remove(struct platform_device *pdev)
component_del(&pdev->dev, &sun6i_dsi_ops);
mipi_dsi_host_unregister(&dsi->host);
pm_runtime_disable(dev);
- sun6i_dphy_remove(dsi);
clk_rate_exclusive_put(dsi->mod_clk);

return 0;
diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
index dbbc5b3ecbda..a07090579f84 100644
--- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
+++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
@@ -13,13 +13,6 @@
#include <drm/drm_encoder.h>
#include <drm/drm_mipi_dsi.h>

-struct sun6i_dphy {
- struct clk *bus_clk;
- struct clk *mod_clk;
- struct regmap *regs;
- struct reset_control *reset;
-};
-
struct sun6i_dsi {
struct drm_connector connector;
struct drm_encoder encoder;
@@ -29,7 +22,7 @@ struct sun6i_dsi {
struct clk *mod_clk;
struct regmap *regs;
struct reset_control *reset;
- struct sun6i_dphy *dphy;
+ struct phy *dphy;

struct device *dev;
struct sun4i_drv *drv;
@@ -52,12 +45,4 @@ static inline struct sun6i_dsi *encoder_to_sun6i_dsi(const struct drm_encoder *e
return container_of(encoder, struct sun6i_dsi, encoder);
};

-int sun6i_dphy_probe(struct sun6i_dsi *dsi, struct device_node *node);
-int sun6i_dphy_remove(struct sun6i_dsi *dsi);
-
-int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes);
-int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes);
-int sun6i_dphy_power_off(struct sun6i_dphy *dphy);
-int sun6i_dphy_exit(struct sun6i_dphy *dphy);
-
#endif /* _SUN6I_MIPI_DSI_H_ */
--
git-series 0.9.1

2019-01-21 15:49:27

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v5 1/9] phy: dphy: Remove unused header

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

Suggested-by: Sakari Ailus <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
Signed-off-by: Maxime Ripard <[email protected]>
---
include/linux/phy/phy-mipi-dphy.h | 2 --
1 file changed, 2 deletions(-)

diff --git a/include/linux/phy/phy-mipi-dphy.h b/include/linux/phy/phy-mipi-dphy.h
index c08aacc0ac35..9cf97cd1d303 100644
--- a/include/linux/phy/phy-mipi-dphy.h
+++ b/include/linux/phy/phy-mipi-dphy.h
@@ -6,8 +6,6 @@
#ifndef __PHY_MIPI_DPHY_H_
#define __PHY_MIPI_DPHY_H_

-#include <video/videomode.h>
-
/**
* struct phy_configure_opts_mipi_dphy - MIPI D-PHY configuration set
*
--
git-series 0.9.1

2019-01-21 15:49:33

by Maxime Ripard

[permalink] [raw]
Subject: [PATCH v5 3/9] phy: dphy: Clarify lanes parameter documentation

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

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

Suggested-by: Sakari Ailus <[email protected]>
Acked-by: Sakari Ailus <[email protected]>
Signed-off-by: Maxime Ripard <[email protected]>
---
include/linux/phy/phy-mipi-dphy.h | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

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

2019-02-04 10:11:03

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices



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

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

Thanks
Kishon

>
> Let me know what you think,
> Maxime
>
> Changes from v4:
> - Removed regression on the variable calculation
> - Fixed the wakeup unit
> - Collected Sean Acked-by on the last patch
> - Collected Sakari Reviewed-by on the first patch
>
> Changes from v3
> - Rebased on 5.0-rc1
> - Added the fixes suggested by Sakari
>
> Changes from v2:
> - Rebased on next
> - Changed the interface to accomodate for the new submodes
> - Changed the timings units from nanoseconds to picoseconds
> - Added minimum and maximum boundaries to the documentation
> - Moved the clock enabling to phy_power_on in the Cadence DPHY driver
> - Exported the phy_configure and phy_validate symbols
> - Rework the phy pll divider computation in the cadence dphy driver
>
> Changes from v1:
> - Rebased on top of 4.20-rc1
> - Removed the bus mode and timings parameters from the MIPI D-PHY
> parameters, since that shouldn't have any impact on the PHY itself.
> - Reworked the Cadence DSI and D-PHY drivers to take this into account.
> - Remove the mode parameter from phy_configure
> - Added phy_configure and phy_validate stubs
> - Return -EOPNOTSUPP in phy_configure and phy_validate when the operation
> is not implemented
>
> Maxime Ripard (9):
> phy: dphy: Remove unused header
> phy: dphy: Change units of wakeup and init parameters
> phy: dphy: Clarify lanes parameter documentation
> sun6i: dsi: Convert to generic phy handling
> phy: Move Allwinner A31 D-PHY driver to drivers/phy/
> drm/bridge: cdns: Separate DSI and D-PHY configuration
> dt-bindings: phy: Move the Cadence D-PHY bindings
> phy: Add Cadence D-PHY support
> drm/bridge: cdns: Convert to phy framework
>
> Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt | 21 +-
> Documentation/devicetree/bindings/phy/cdns,dphy.txt | 20 +-
> drivers/gpu/drm/bridge/Kconfig | 1 +-
> drivers/gpu/drm/bridge/cdns-dsi.c | 538 +------
> drivers/gpu/drm/sun4i/Kconfig | 3 +-
> drivers/gpu/drm/sun4i/Makefile | 5 +-
> drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c | 292 +----
> drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 31 +-
> drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h | 17 +-
> drivers/phy/allwinner/Kconfig | 12 +-
> drivers/phy/allwinner/Makefile | 1 +-
> drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 318 ++++-
> drivers/phy/cadence/Kconfig | 13 +-
> drivers/phy/cadence/Makefile | 1 +-
> drivers/phy/cadence/cdns-dphy.c | 389 +++++-
> drivers/phy/phy-core-mipi-dphy.c | 8 +-
> include/linux/phy/phy-mipi-dphy.h | 13 +-
> 17 files changed, 894 insertions(+), 789 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/phy/cdns,dphy.txt
> delete mode 100644 drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
> create mode 100644 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
> create mode 100644 drivers/phy/cadence/cdns-dphy.c
>
> base-commit: bfeffd155283772bbe78c6a05dec7c0128ee500c
>

2019-02-05 08:46:48

by Daniel Vetter

[permalink] [raw]
Subject: Re: [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices

On Mon, Feb 04, 2019 at 03:33:31PM +0530, Kishon Vijay Abraham I wrote:
>
>
> On 21/01/19 9:15 PM, Maxime Ripard wrote:
> > Hi,
> >
> > Here is a set of patches to allow the phy framework consumers to test and
> > apply runtime configurations.
> >
> > This is needed to support more phy classes that require tuning based on
> > parameters depending on the current use case of the device, in addition to
> > the power state management already provided by the current functions.
> >
> > A first test bed for that API are the MIPI D-PHY devices. There's a number
> > of solutions that have been used so far to support these phy, most of the
> > time being an ad-hoc driver in the consumer.
> >
> > That approach has a big shortcoming though, which is that this is quite
> > difficult to deal with consumers integrated with multiple variants of phy,
> > of multiple consumers integrated with the same phy.
> >
> > The latter case can be found in the Cadence DSI bridge, and the CSI
> > transceiver and receivers. All of them are integrated with the same phy, or
> > can be integrated with different phy, depending on the implementation.
> >
> > I've looked at all the MIPI DSI drivers I could find, and gathered all the
> > parameters I could find. The interface should be complete, and most of the
> > drivers can be converted in the future. The current set converts two of
> > them: the above mentionned Cadence DSI driver so that the v4l2 drivers can
> > use them, and the Allwinner MIPI-DSI driver.
>
> Can the PHY changes go independently of the consumer drivers? or else I'll need
> ACKs from the GPU MAINTAINER.

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

>
> Thanks
> Kishon
>
> >
> > Let me know what you think,
> > Maxime
> >
> > Changes from v4:
> > - Removed regression on the variable calculation
> > - Fixed the wakeup unit
> > - Collected Sean Acked-by on the last patch
> > - Collected Sakari Reviewed-by on the first patch
> >
> > Changes from v3
> > - Rebased on 5.0-rc1
> > - Added the fixes suggested by Sakari
> >
> > Changes from v2:
> > - Rebased on next
> > - Changed the interface to accomodate for the new submodes
> > - Changed the timings units from nanoseconds to picoseconds
> > - Added minimum and maximum boundaries to the documentation
> > - Moved the clock enabling to phy_power_on in the Cadence DPHY driver
> > - Exported the phy_configure and phy_validate symbols
> > - Rework the phy pll divider computation in the cadence dphy driver
> >
> > Changes from v1:
> > - Rebased on top of 4.20-rc1
> > - Removed the bus mode and timings parameters from the MIPI D-PHY
> > parameters, since that shouldn't have any impact on the PHY itself.
> > - Reworked the Cadence DSI and D-PHY drivers to take this into account.
> > - Remove the mode parameter from phy_configure
> > - Added phy_configure and phy_validate stubs
> > - Return -EOPNOTSUPP in phy_configure and phy_validate when the operation
> > is not implemented
> >
> > Maxime Ripard (9):
> > phy: dphy: Remove unused header
> > phy: dphy: Change units of wakeup and init parameters
> > phy: dphy: Clarify lanes parameter documentation
> > sun6i: dsi: Convert to generic phy handling
> > phy: Move Allwinner A31 D-PHY driver to drivers/phy/
> > drm/bridge: cdns: Separate DSI and D-PHY configuration
> > dt-bindings: phy: Move the Cadence D-PHY bindings
> > phy: Add Cadence D-PHY support
> > drm/bridge: cdns: Convert to phy framework
> >
> > Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt | 21 +-
> > Documentation/devicetree/bindings/phy/cdns,dphy.txt | 20 +-
> > drivers/gpu/drm/bridge/Kconfig | 1 +-
> > drivers/gpu/drm/bridge/cdns-dsi.c | 538 +------
> > drivers/gpu/drm/sun4i/Kconfig | 3 +-
> > drivers/gpu/drm/sun4i/Makefile | 5 +-
> > drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c | 292 +----
> > drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 31 +-
> > drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h | 17 +-
> > drivers/phy/allwinner/Kconfig | 12 +-
> > drivers/phy/allwinner/Makefile | 1 +-
> > drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 318 ++++-
> > drivers/phy/cadence/Kconfig | 13 +-
> > drivers/phy/cadence/Makefile | 1 +-
> > drivers/phy/cadence/cdns-dphy.c | 389 +++++-
> > drivers/phy/phy-core-mipi-dphy.c | 8 +-
> > include/linux/phy/phy-mipi-dphy.h | 13 +-
> > 17 files changed, 894 insertions(+), 789 deletions(-)
> > create mode 100644 Documentation/devicetree/bindings/phy/cdns,dphy.txt
> > delete mode 100644 drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
> > create mode 100644 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
> > create mode 100644 drivers/phy/cadence/cdns-dphy.c
> >
> > base-commit: bfeffd155283772bbe78c6a05dec7c0128ee500c
> >
> _______________________________________________
> dri-devel mailing list
> [email protected]
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

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

2019-02-06 12:15:20

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices

Hi,

On 05/02/19 2:16 PM, Daniel Vetter wrote:
> On Mon, Feb 04, 2019 at 03:33:31PM +0530, Kishon Vijay Abraham I wrote:
>>
>>
>> On 21/01/19 9:15 PM, Maxime Ripard wrote:
>>> Hi,
>>>
>>> Here is a set of patches to allow the phy framework consumers to test and
>>> apply runtime configurations.
>>>
>>> This is needed to support more phy classes that require tuning based on
>>> parameters depending on the current use case of the device, in addition to
>>> the power state management already provided by the current functions.
>>>
>>> A first test bed for that API are the MIPI D-PHY devices. There's a number
>>> of solutions that have been used so far to support these phy, most of the
>>> time being an ad-hoc driver in the consumer.
>>>
>>> That approach has a big shortcoming though, which is that this is quite
>>> difficult to deal with consumers integrated with multiple variants of phy,
>>> of multiple consumers integrated with the same phy.
>>>
>>> The latter case can be found in the Cadence DSI bridge, and the CSI
>>> transceiver and receivers. All of them are integrated with the same phy, or
>>> can be integrated with different phy, depending on the implementation.
>>>
>>> I've looked at all the MIPI DSI drivers I could find, and gathered all the
>>> parameters I could find. The interface should be complete, and most of the
>>> drivers can be converted in the future. The current set converts two of
>>> them: the above mentionned Cadence DSI driver so that the v4l2 drivers can
>>> use them, and the Allwinner MIPI-DSI driver.
>>
>> Can the PHY changes go independently of the consumer drivers? or else I'll need
>> ACKs from the GPU MAINTAINER.
>
> Maxime is a gpu maintainer, so you're all good :-)

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

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

Thanks
Kishon
> -Daniel
>
>>
>> Thanks
>> Kishon
>>
>>>
>>> Let me know what you think,
>>> Maxime
>>>
>>> Changes from v4:
>>> - Removed regression on the variable calculation
>>> - Fixed the wakeup unit
>>> - Collected Sean Acked-by on the last patch
>>> - Collected Sakari Reviewed-by on the first patch
>>>
>>> Changes from v3
>>> - Rebased on 5.0-rc1
>>> - Added the fixes suggested by Sakari
>>>
>>> Changes from v2:
>>> - Rebased on next
>>> - Changed the interface to accomodate for the new submodes
>>> - Changed the timings units from nanoseconds to picoseconds
>>> - Added minimum and maximum boundaries to the documentation
>>> - Moved the clock enabling to phy_power_on in the Cadence DPHY driver
>>> - Exported the phy_configure and phy_validate symbols
>>> - Rework the phy pll divider computation in the cadence dphy driver
>>>
>>> Changes from v1:
>>> - Rebased on top of 4.20-rc1
>>> - Removed the bus mode and timings parameters from the MIPI D-PHY
>>> parameters, since that shouldn't have any impact on the PHY itself.
>>> - Reworked the Cadence DSI and D-PHY drivers to take this into account.
>>> - Remove the mode parameter from phy_configure
>>> - Added phy_configure and phy_validate stubs
>>> - Return -EOPNOTSUPP in phy_configure and phy_validate when the operation
>>> is not implemented
>>>
>>> Maxime Ripard (9):
>>> phy: dphy: Remove unused header
>>> phy: dphy: Change units of wakeup and init parameters
>>> phy: dphy: Clarify lanes parameter documentation
>>> sun6i: dsi: Convert to generic phy handling
>>> phy: Move Allwinner A31 D-PHY driver to drivers/phy/
>>> drm/bridge: cdns: Separate DSI and D-PHY configuration
>>> dt-bindings: phy: Move the Cadence D-PHY bindings
>>> phy: Add Cadence D-PHY support
>>> drm/bridge: cdns: Convert to phy framework
>>>
>>> Documentation/devicetree/bindings/display/bridge/cdns,dsi.txt | 21 +-
>>> Documentation/devicetree/bindings/phy/cdns,dphy.txt | 20 +-
>>> drivers/gpu/drm/bridge/Kconfig | 1 +-
>>> drivers/gpu/drm/bridge/cdns-dsi.c | 538 +------
>>> drivers/gpu/drm/sun4i/Kconfig | 3 +-
>>> drivers/gpu/drm/sun4i/Makefile | 5 +-
>>> drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c | 292 +----
>>> drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 31 +-
>>> drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h | 17 +-
>>> drivers/phy/allwinner/Kconfig | 12 +-
>>> drivers/phy/allwinner/Makefile | 1 +-
>>> drivers/phy/allwinner/phy-sun6i-mipi-dphy.c | 318 ++++-
>>> drivers/phy/cadence/Kconfig | 13 +-
>>> drivers/phy/cadence/Makefile | 1 +-
>>> drivers/phy/cadence/cdns-dphy.c | 389 +++++-
>>> drivers/phy/phy-core-mipi-dphy.c | 8 +-
>>> include/linux/phy/phy-mipi-dphy.h | 13 +-
>>> 17 files changed, 894 insertions(+), 789 deletions(-)
>>> create mode 100644 Documentation/devicetree/bindings/phy/cdns,dphy.txt
>>> delete mode 100644 drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
>>> create mode 100644 drivers/phy/allwinner/phy-sun6i-mipi-dphy.c
>>> create mode 100644 drivers/phy/cadence/cdns-dphy.c
>>>
>>> base-commit: bfeffd155283772bbe78c6a05dec7c0128ee500c
>>>
>> _______________________________________________
>> dri-devel mailing list
>> [email protected]
>> https://lists.freedesktop.org/mailman/listinfo/dri-devel
>

2019-02-06 12:26:27

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices

Hi Kishon,

On Wed, Feb 06, 2019 at 05:43:12PM +0530, Kishon Vijay Abraham I wrote:
> On 05/02/19 2:16 PM, Daniel Vetter wrote:
> > On Mon, Feb 04, 2019 at 03:33:31PM +0530, Kishon Vijay Abraham I wrote:
> >>
> >>
> >> On 21/01/19 9:15 PM, Maxime Ripard wrote:
> >>> Hi,
> >>>
> >>> Here is a set of patches to allow the phy framework consumers to test and
> >>> apply runtime configurations.
> >>>
> >>> This is needed to support more phy classes that require tuning based on
> >>> parameters depending on the current use case of the device, in addition to
> >>> the power state management already provided by the current functions.
> >>>
> >>> A first test bed for that API are the MIPI D-PHY devices. There's a number
> >>> of solutions that have been used so far to support these phy, most of the
> >>> time being an ad-hoc driver in the consumer.
> >>>
> >>> That approach has a big shortcoming though, which is that this is quite
> >>> difficult to deal with consumers integrated with multiple variants of phy,
> >>> of multiple consumers integrated with the same phy.
> >>>
> >>> The latter case can be found in the Cadence DSI bridge, and the CSI
> >>> transceiver and receivers. All of them are integrated with the same phy, or
> >>> can be integrated with different phy, depending on the implementation.
> >>>
> >>> I've looked at all the MIPI DSI drivers I could find, and gathered all the
> >>> parameters I could find. The interface should be complete, and most of the
> >>> drivers can be converted in the future. The current set converts two of
> >>> them: the above mentionned Cadence DSI driver so that the v4l2 drivers can
> >>> use them, and the Allwinner MIPI-DSI driver.
> >>
> >> Can the PHY changes go independently of the consumer drivers? or else I'll need
> >> ACKs from the GPU MAINTAINER.
> >
> > Maxime is a gpu maintainer, so you're all good :-)
>
> cool.. I've merged all the patches except drm/bridge.
>
> Please see if everything looks okay once it shows up in phy -next (give a day)

Thanks!

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

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

Maxime

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


Attachments:
(No filename) (2.39 kB)
signature.asc (235.00 B)
Download all attachments

2019-02-06 12:31:47

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices

Hi,

On 06/02/19 5:55 PM, Maxime Ripard wrote:
> Hi Kishon,
>
> On Wed, Feb 06, 2019 at 05:43:12PM +0530, Kishon Vijay Abraham I wrote:
>> On 05/02/19 2:16 PM, Daniel Vetter wrote:
>>> On Mon, Feb 04, 2019 at 03:33:31PM +0530, Kishon Vijay Abraham I wrote:
>>>>
>>>>
>>>> On 21/01/19 9:15 PM, Maxime Ripard wrote:
>>>>> Hi,
>>>>>
>>>>> Here is a set of patches to allow the phy framework consumers to test and
>>>>> apply runtime configurations.
>>>>>
>>>>> This is needed to support more phy classes that require tuning based on
>>>>> parameters depending on the current use case of the device, in addition to
>>>>> the power state management already provided by the current functions.
>>>>>
>>>>> A first test bed for that API are the MIPI D-PHY devices. There's a number
>>>>> of solutions that have been used so far to support these phy, most of the
>>>>> time being an ad-hoc driver in the consumer.
>>>>>
>>>>> That approach has a big shortcoming though, which is that this is quite
>>>>> difficult to deal with consumers integrated with multiple variants of phy,
>>>>> of multiple consumers integrated with the same phy.
>>>>>
>>>>> The latter case can be found in the Cadence DSI bridge, and the CSI
>>>>> transceiver and receivers. All of them are integrated with the same phy, or
>>>>> can be integrated with different phy, depending on the implementation.
>>>>>
>>>>> I've looked at all the MIPI DSI drivers I could find, and gathered all the
>>>>> parameters I could find. The interface should be complete, and most of the
>>>>> drivers can be converted in the future. The current set converts two of
>>>>> them: the above mentionned Cadence DSI driver so that the v4l2 drivers can
>>>>> use them, and the Allwinner MIPI-DSI driver.
>>>>
>>>> Can the PHY changes go independently of the consumer drivers? or else I'll need
>>>> ACKs from the GPU MAINTAINER.
>>>
>>> Maxime is a gpu maintainer, so you're all good :-)
>>
>> cool.. I've merged all the patches except drm/bridge.
>>
>> Please see if everything looks okay once it shows up in phy -next (give a day)
>
> Thanks!
>
> If possible (and if that's still an option), it would be better if the
> sun6i related patches (patches 4 and 5) would go through the DRM tree
> (with your Acked-by of course).
>
> We have a number of patches in flight that have a decent chance to
> conflict with patch 4.

Sure. Dropped patches 4 and 5 from my tree.

Thanks
Kishon

2019-02-07 08:16:21

by Paul Kocialkowski

[permalink] [raw]
Subject: Re: [PATCH v5 4/9] sun6i: dsi: Convert to generic phy handling

Hi,

On Mon, 2019-01-21 at 16:45 +0100, Maxime Ripard wrote:
> Now that we have everything in place in the PHY framework to deal in a
> generic way with MIPI D-PHY phys, let's convert our PHY driver and its
> associated DSI driver to that new API.
>
> Signed-off-by: Maxime Ripard <[email protected]>

Reviewed-by: Paul Kocialkowski <[email protected]>

Cheers,

Paul

> ---
> drivers/gpu/drm/sun4i/Kconfig | 11 +-
> drivers/gpu/drm/sun4i/Makefile | 6 +-
> drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c | 164 ++++++++++++++-----------
> drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c | 31 ++---
> drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h | 17 +---
> 5 files changed, 126 insertions(+), 103 deletions(-)
>
> diff --git a/drivers/gpu/drm/sun4i/Kconfig b/drivers/gpu/drm/sun4i/Kconfig
> index c2c042287c19..2b8db82c4bab 100644
> --- a/drivers/gpu/drm/sun4i/Kconfig
> +++ b/drivers/gpu/drm/sun4i/Kconfig
> @@ -45,10 +45,19 @@ config DRM_SUN6I_DSI
> default MACH_SUN8I
> select CRC_CCITT
> select DRM_MIPI_DSI
> + select DRM_SUN6I_DPHY
> help
> Choose this option if you want have an Allwinner SoC with
> MIPI-DSI support. If M is selected the module will be called
> - sun6i-dsi
> + sun6i_mipi_dsi.
> +
> +config DRM_SUN6I_DPHY
> + tristate "Allwinner A31 MIPI D-PHY Support"
> + select GENERIC_PHY_MIPI_DPHY
> + help
> + Choose this option if you have an Allwinner SoC with
> + MIPI-DSI support. If M is selected, the module will be
> + called sun6i_mipi_dphy.
>
> config DRM_SUN8I_DW_HDMI
> tristate "Support for Allwinner version of DesignWare HDMI"
> diff --git a/drivers/gpu/drm/sun4i/Makefile b/drivers/gpu/drm/sun4i/Makefile
> index 0eb38ac8e86e..1e2320d824b5 100644
> --- a/drivers/gpu/drm/sun4i/Makefile
> +++ b/drivers/gpu/drm/sun4i/Makefile
> @@ -24,9 +24,6 @@ sun4i-tcon-y += sun4i_lvds.o
> sun4i-tcon-y += sun4i_tcon.o
> sun4i-tcon-y += sun4i_rgb.o
>
> -sun6i-dsi-y += sun6i_mipi_dphy.o
> -sun6i-dsi-y += sun6i_mipi_dsi.o
> -
> obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o
> obj-$(CONFIG_DRM_SUN4I) += sun4i-tcon.o
> obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o
> @@ -37,7 +34,8 @@ ifdef CONFIG_DRM_SUN4I_BACKEND
> obj-$(CONFIG_DRM_SUN4I) += sun4i-frontend.o
> endif
> obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o
> -obj-$(CONFIG_DRM_SUN6I_DSI) += sun6i-dsi.o
> +obj-$(CONFIG_DRM_SUN6I_DPHY) += sun6i_mipi_dphy.o
> +obj-$(CONFIG_DRM_SUN6I_DSI) += sun6i_mipi_dsi.o
> obj-$(CONFIG_DRM_SUN8I_DW_HDMI) += sun8i-drm-hdmi.o
> obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o
> obj-$(CONFIG_DRM_SUN8I_TCON_TOP) += sun8i_tcon_top.o
> diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
> index e4d19431fa0e..79c8af5c7c1d 100644
> --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
> +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
> @@ -8,11 +8,14 @@
>
> #include <linux/bitops.h>
> #include <linux/clk.h>
> +#include <linux/module.h>
> #include <linux/of_address.h>
> +#include <linux/platform_device.h>
> #include <linux/regmap.h>
> #include <linux/reset.h>
>
> -#include "sun6i_mipi_dsi.h"
> +#include <linux/phy/phy.h>
> +#include <linux/phy/phy-mipi-dphy.h>
>
> #define SUN6I_DPHY_GCTL_REG 0x00
> #define SUN6I_DPHY_GCTL_LANE_NUM(n) ((((n) - 1) & 3) << 4)
> @@ -81,12 +84,46 @@
>
> #define SUN6I_DPHY_DBG5_REG 0xf4
>
> -int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes)
> +struct sun6i_dphy {
> + struct clk *bus_clk;
> + struct clk *mod_clk;
> + struct regmap *regs;
> + struct reset_control *reset;
> +
> + struct phy *phy;
> + struct phy_configure_opts_mipi_dphy config;
> +};
> +
> +static int sun6i_dphy_init(struct phy *phy)
> {
> + struct sun6i_dphy *dphy = phy_get_drvdata(phy);
> +
> reset_control_deassert(dphy->reset);
> clk_prepare_enable(dphy->mod_clk);
> clk_set_rate_exclusive(dphy->mod_clk, 150000000);
>
> + return 0;
> +}
> +
> +static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
> +{
> + struct sun6i_dphy *dphy = phy_get_drvdata(phy);
> + int ret;
> +
> + ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
> + if (ret)
> + return ret;
> +
> + memcpy(&dphy->config, opts, sizeof(dphy->config));
> +
> + return 0;
> +}
> +
> +static int sun6i_dphy_power_on(struct phy *phy)
> +{
> + struct sun6i_dphy *dphy = phy_get_drvdata(phy);
> + u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
> +
> regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
> SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);
>
> @@ -111,16 +148,9 @@ int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes)
> SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
>
> regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
> - SUN6I_DPHY_GCTL_LANE_NUM(lanes) |
> + SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
> SUN6I_DPHY_GCTL_EN);
>
> - return 0;
> -}
> -
> -int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes)
> -{
> - u8 lanes_mask = GENMASK(lanes - 1, 0);
> -
> regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
> SUN6I_DPHY_ANA0_REG_PWS |
> SUN6I_DPHY_ANA0_REG_DMPC |
> @@ -181,16 +211,20 @@ int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes)
> return 0;
> }
>
> -int sun6i_dphy_power_off(struct sun6i_dphy *dphy)
> +static int sun6i_dphy_power_off(struct phy *phy)
> {
> + struct sun6i_dphy *dphy = phy_get_drvdata(phy);
> +
> regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
> SUN6I_DPHY_ANA1_REG_VTTMODE, 0);
>
> return 0;
> }
>
> -int sun6i_dphy_exit(struct sun6i_dphy *dphy)
> +static int sun6i_dphy_exit(struct phy *phy)
> {
> + struct sun6i_dphy *dphy = phy_get_drvdata(phy);
> +
> clk_rate_exclusive_put(dphy->mod_clk);
> clk_disable_unprepare(dphy->mod_clk);
> reset_control_assert(dphy->reset);
> @@ -198,6 +232,15 @@ int sun6i_dphy_exit(struct sun6i_dphy *dphy)
> return 0;
> }
>
> +
> +static struct phy_ops sun6i_dphy_ops = {
> + .configure = sun6i_dphy_configure,
> + .power_on = sun6i_dphy_power_on,
> + .power_off = sun6i_dphy_power_off,
> + .init = sun6i_dphy_init,
> + .exit = sun6i_dphy_exit,
> +};
> +
> static struct regmap_config sun6i_dphy_regmap_config = {
> .reg_bits = 32,
> .val_bits = 32,
> @@ -206,87 +249,70 @@ static struct regmap_config sun6i_dphy_regmap_config = {
> .name = "mipi-dphy",
> };
>
> -static const struct of_device_id sun6i_dphy_of_table[] = {
> - { .compatible = "allwinner,sun6i-a31-mipi-dphy" },
> - { }
> -};
> -
> -int sun6i_dphy_probe(struct sun6i_dsi *dsi, struct device_node *node)
> +static int sun6i_dphy_probe(struct platform_device *pdev)
> {
> + struct phy_provider *phy_provider;
> struct sun6i_dphy *dphy;
> - struct resource res;
> + struct resource *res;
> void __iomem *regs;
> - int ret;
> -
> - if (!of_match_node(sun6i_dphy_of_table, node)) {
> - dev_err(dsi->dev, "Incompatible D-PHY\n");
> - return -EINVAL;
> - }
>
> - dphy = devm_kzalloc(dsi->dev, sizeof(*dphy), GFP_KERNEL);
> + dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
> if (!dphy)
> return -ENOMEM;
>
> - ret = of_address_to_resource(node, 0, &res);
> - if (ret) {
> - dev_err(dsi->dev, "phy: Couldn't get our resources\n");
> - return ret;
> - }
> -
> - regs = devm_ioremap_resource(dsi->dev, &res);
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + regs = devm_ioremap_resource(&pdev->dev, res);
> if (IS_ERR(regs)) {
> - dev_err(dsi->dev, "Couldn't map the DPHY encoder registers\n");
> + dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n");
> return PTR_ERR(regs);
> }
>
> - dphy->regs = devm_regmap_init_mmio(dsi->dev, regs,
> - &sun6i_dphy_regmap_config);
> + dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus",
> + regs, &sun6i_dphy_regmap_config);
> if (IS_ERR(dphy->regs)) {
> - dev_err(dsi->dev, "Couldn't create the DPHY encoder regmap\n");
> + dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n");
> return PTR_ERR(dphy->regs);
> }
>
> - dphy->reset = of_reset_control_get_shared(node, NULL);
> + dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
> if (IS_ERR(dphy->reset)) {
> - dev_err(dsi->dev, "Couldn't get our reset line\n");
> + dev_err(&pdev->dev, "Couldn't get our reset line\n");
> return PTR_ERR(dphy->reset);
> }
>
> - dphy->bus_clk = of_clk_get_by_name(node, "bus");
> - if (IS_ERR(dphy->bus_clk)) {
> - dev_err(dsi->dev, "Couldn't get the DPHY bus clock\n");
> - ret = PTR_ERR(dphy->bus_clk);
> - goto err_free_reset;
> - }
> - regmap_mmio_attach_clk(dphy->regs, dphy->bus_clk);
> -
> - dphy->mod_clk = of_clk_get_by_name(node, "mod");
> + dphy->mod_clk = devm_clk_get(&pdev->dev, "mod");
> if (IS_ERR(dphy->mod_clk)) {
> - dev_err(dsi->dev, "Couldn't get the DPHY mod clock\n");
> - ret = PTR_ERR(dphy->mod_clk);
> - goto err_free_bus;
> + dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n");
> + return PTR_ERR(dphy->mod_clk);
> }
>
> - dsi->dphy = dphy;
> + dphy->phy = devm_phy_create(&pdev->dev, NULL, &sun6i_dphy_ops);
> + if (IS_ERR(dphy->phy)) {
> + dev_err(&pdev->dev, "failed to create PHY\n");
> + return PTR_ERR(dphy->phy);
> + }
>
> - return 0;
> + phy_set_drvdata(dphy->phy, dphy);
> + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
>
> -err_free_bus:
> - regmap_mmio_detach_clk(dphy->regs);
> - clk_put(dphy->bus_clk);
> -err_free_reset:
> - reset_control_put(dphy->reset);
> - return ret;
> + return PTR_ERR_OR_ZERO(phy_provider);
> }
>
> -int sun6i_dphy_remove(struct sun6i_dsi *dsi)
> -{
> - struct sun6i_dphy *dphy = dsi->dphy;
> -
> - regmap_mmio_detach_clk(dphy->regs);
> - clk_put(dphy->mod_clk);
> - clk_put(dphy->bus_clk);
> - reset_control_put(dphy->reset);
> +static const struct of_device_id sun6i_dphy_of_table[] = {
> + { .compatible = "allwinner,sun6i-a31-mipi-dphy" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table);
> +
> +static struct platform_driver sun6i_dphy_platform_driver = {
> + .probe = sun6i_dphy_probe,
> + .driver = {
> + .name = "sun6i-mipi-dphy",
> + .of_match_table = sun6i_dphy_of_table,
> + },
> +};
> +module_platform_driver(sun6i_dphy_platform_driver);
>
> - return 0;
> -}
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>");
> +MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> index e3b34a345546..7bbce7708265 100644
> --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
> @@ -16,6 +16,7 @@
> #include <linux/slab.h>
>
> #include <linux/phy/phy.h>
> +#include <linux/phy/phy-mipi-dphy.h>
>
> #include <drm/drmP.h>
> #include <drm/drm_atomic_helper.h>
> @@ -616,6 +617,8 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
> struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
> struct sun6i_dsi *dsi = encoder_to_sun6i_dsi(encoder);
> struct mipi_dsi_device *device = dsi->device;
> + union phy_configure_opts opts = { 0 };
> + struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy;
> u16 delay;
>
> DRM_DEBUG_DRIVER("Enabling DSI output\n");
> @@ -634,8 +637,15 @@ static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder)
> sun6i_dsi_setup_format(dsi, mode);
> sun6i_dsi_setup_timings(dsi, mode);
>
> - sun6i_dphy_init(dsi->dphy, device->lanes);
> - sun6i_dphy_power_on(dsi->dphy, device->lanes);
> + phy_init(dsi->dphy);
> +
> + phy_mipi_dphy_get_default_config(mode->clock * 1000,
> + mipi_dsi_pixel_format_to_bpp(device->format),
> + device->lanes, cfg);
> +
> + phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY);
> + phy_configure(dsi->dphy, &opts);
> + phy_power_on(dsi->dphy);
>
> if (!IS_ERR(dsi->panel))
> drm_panel_prepare(dsi->panel);
> @@ -673,8 +683,8 @@ static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder)
> drm_panel_unprepare(dsi->panel);
> }
>
> - sun6i_dphy_power_off(dsi->dphy);
> - sun6i_dphy_exit(dsi->dphy);
> + phy_power_off(dsi->dphy);
> + phy_exit(dsi->dphy);
>
> pm_runtime_put(dsi->dev);
> }
> @@ -967,7 +977,6 @@ static const struct component_ops sun6i_dsi_ops = {
> static int sun6i_dsi_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> - struct device_node *dphy_node;
> struct sun6i_dsi *dsi;
> struct resource *res;
> void __iomem *base;
> @@ -1013,10 +1022,8 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
> */
> clk_set_rate_exclusive(dsi->mod_clk, 297000000);
>
> - dphy_node = of_parse_phandle(dev->of_node, "phys", 0);
> - ret = sun6i_dphy_probe(dsi, dphy_node);
> - of_node_put(dphy_node);
> - if (ret) {
> + dsi->dphy = devm_phy_get(dev, "dphy");
> + if (IS_ERR(dsi->dphy)) {
> dev_err(dev, "Couldn't get the MIPI D-PHY\n");
> goto err_unprotect_clk;
> }
> @@ -1026,7 +1033,7 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
> ret = mipi_dsi_host_register(&dsi->host);
> if (ret) {
> dev_err(dev, "Couldn't register MIPI-DSI host\n");
> - goto err_remove_phy;
> + goto err_pm_disable;
> }
>
> ret = component_add(&pdev->dev, &sun6i_dsi_ops);
> @@ -1039,9 +1046,8 @@ static int sun6i_dsi_probe(struct platform_device *pdev)
>
> err_remove_dsi_host:
> mipi_dsi_host_unregister(&dsi->host);
> -err_remove_phy:
> +err_pm_disable:
> pm_runtime_disable(dev);
> - sun6i_dphy_remove(dsi);
> err_unprotect_clk:
> clk_rate_exclusive_put(dsi->mod_clk);
> return ret;
> @@ -1055,7 +1061,6 @@ static int sun6i_dsi_remove(struct platform_device *pdev)
> component_del(&pdev->dev, &sun6i_dsi_ops);
> mipi_dsi_host_unregister(&dsi->host);
> pm_runtime_disable(dev);
> - sun6i_dphy_remove(dsi);
> clk_rate_exclusive_put(dsi->mod_clk);
>
> return 0;
> diff --git a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
> index dbbc5b3ecbda..a07090579f84 100644
> --- a/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
> +++ b/drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
> @@ -13,13 +13,6 @@
> #include <drm/drm_encoder.h>
> #include <drm/drm_mipi_dsi.h>
>
> -struct sun6i_dphy {
> - struct clk *bus_clk;
> - struct clk *mod_clk;
> - struct regmap *regs;
> - struct reset_control *reset;
> -};
> -
> struct sun6i_dsi {
> struct drm_connector connector;
> struct drm_encoder encoder;
> @@ -29,7 +22,7 @@ struct sun6i_dsi {
> struct clk *mod_clk;
> struct regmap *regs;
> struct reset_control *reset;
> - struct sun6i_dphy *dphy;
> + struct phy *dphy;
>
> struct device *dev;
> struct sun4i_drv *drv;
> @@ -52,12 +45,4 @@ static inline struct sun6i_dsi *encoder_to_sun6i_dsi(const struct drm_encoder *e
> return container_of(encoder, struct sun6i_dsi, encoder);
> };
>
> -int sun6i_dphy_probe(struct sun6i_dsi *dsi, struct device_node *node);
> -int sun6i_dphy_remove(struct sun6i_dsi *dsi);
> -
> -int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes);
> -int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes);
> -int sun6i_dphy_power_off(struct sun6i_dphy *dphy);
> -int sun6i_dphy_exit(struct sun6i_dphy *dphy);
> -
> #endif /* _SUN6I_MIPI_DSI_H_ */
--
Paul Kocialkowski, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com


2019-02-07 08:19:36

by Paul Kocialkowski

[permalink] [raw]
Subject: Re: [PATCH v5 5/9] phy: Move Allwinner A31 D-PHY driver to drivers/phy/

Hi,

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

Reviewed-by: Paul Kocialkowski <[email protected]>

Cheers,

Paul

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


2019-02-07 09:18:28

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v5 0/9] phy: Add configuration interface for MIPI D-PHY devices

Hi Kishon,

On Wed, Feb 06, 2019 at 06:00:19PM +0530, Kishon Vijay Abraham I wrote:
> On 06/02/19 5:55 PM, Maxime Ripard wrote:
> > On Wed, Feb 06, 2019 at 05:43:12PM +0530, Kishon Vijay Abraham I wrote:
> >> On 05/02/19 2:16 PM, Daniel Vetter wrote:
> >>> On Mon, Feb 04, 2019 at 03:33:31PM +0530, Kishon Vijay Abraham I wrote:
> >>>>
> >>>>
> >>>> On 21/01/19 9:15 PM, Maxime Ripard wrote:
> >>>>> Hi,
> >>>>>
> >>>>> Here is a set of patches to allow the phy framework consumers to test and
> >>>>> apply runtime configurations.
> >>>>>
> >>>>> This is needed to support more phy classes that require tuning based on
> >>>>> parameters depending on the current use case of the device, in addition to
> >>>>> the power state management already provided by the current functions.
> >>>>>
> >>>>> A first test bed for that API are the MIPI D-PHY devices. There's a number
> >>>>> of solutions that have been used so far to support these phy, most of the
> >>>>> time being an ad-hoc driver in the consumer.
> >>>>>
> >>>>> That approach has a big shortcoming though, which is that this is quite
> >>>>> difficult to deal with consumers integrated with multiple variants of phy,
> >>>>> of multiple consumers integrated with the same phy.
> >>>>>
> >>>>> The latter case can be found in the Cadence DSI bridge, and the CSI
> >>>>> transceiver and receivers. All of them are integrated with the same phy, or
> >>>>> can be integrated with different phy, depending on the implementation.
> >>>>>
> >>>>> I've looked at all the MIPI DSI drivers I could find, and gathered all the
> >>>>> parameters I could find. The interface should be complete, and most of the
> >>>>> drivers can be converted in the future. The current set converts two of
> >>>>> them: the above mentionned Cadence DSI driver so that the v4l2 drivers can
> >>>>> use them, and the Allwinner MIPI-DSI driver.
> >>>>
> >>>> Can the PHY changes go independently of the consumer drivers? or else I'll need
> >>>> ACKs from the GPU MAINTAINER.
> >>>
> >>> Maxime is a gpu maintainer, so you're all good :-)
> >>
> >> cool.. I've merged all the patches except drm/bridge.
> >>
> >> Please see if everything looks okay once it shows up in phy -next (give a day)
> >
> > Thanks!
> >
> > If possible (and if that's still an option), it would be better if the
> > sun6i related patches (patches 4 and 5) would go through the DRM tree
> > (with your Acked-by of course).
> >
> > We have a number of patches in flight that have a decent chance to
> > conflict with patch 4.
>
> Sure. Dropped patches 4 and 5 from my tree.

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

Maxime

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


Attachments:
(No filename) (2.72 kB)
signature.asc (235.00 B)
Download all attachments