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

Paul Kocialkowski
2019-02-07T08:16:13Z

Hi,

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

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

Cheers,

Paul

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