Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit bb3b6fcb authored by Maxime Ripard's avatar Maxime Ripard
Browse files

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.

Reviewed-by: default avatarPaul Kocialkowski <paul.kocialkowski@bootlin.com>
Signed-off-by: default avatarMaxime Ripard <maxime.ripard@bootlin.com>
Link: https://patchwork.freedesktop.org/patch/msgid/dc6450e2978b6dafcc464595ad06204d22d2658f.1548085432.git-series.maxime.ripard@bootlin.com
parent 1eb6ea4a
Loading
Loading
Loading
Loading
+10 −1
Original line number Diff line number Diff line
@@ -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"
+2 −4
Original line number Diff line number Diff line
@@ -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
+95 −69
Original line number Diff line number Diff line
@@ -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");
+18 −13
Original line number Diff line number Diff line
@@ -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;
+1 −16
Original line number Diff line number Diff line
@@ -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_ */