Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
Link: https://patchwork.freedesktop.org/patch/msgid/dc6450e2978b6dafcc464595ad06204d22d2658f.1548085432.git-series.maxime.ripard@bootlin.com

+147 -124
+10 -1
drivers/gpu/drm/sun4i/Kconfig
··· 45 45 default MACH_SUN8I 46 46 select CRC_CCITT 47 47 select DRM_MIPI_DSI 48 + select DRM_SUN6I_DPHY 48 49 help 49 50 Choose this option if you want have an Allwinner SoC with 50 51 MIPI-DSI support. If M is selected the module will be called 51 - sun6i-dsi 52 + sun6i_mipi_dsi. 53 + 54 + config DRM_SUN6I_DPHY 55 + tristate "Allwinner A31 MIPI D-PHY Support" 56 + select GENERIC_PHY_MIPI_DPHY 57 + help 58 + Choose this option if you have an Allwinner SoC with 59 + MIPI-DSI support. If M is selected, the module will be 60 + called sun6i_mipi_dphy. 52 61 53 62 config DRM_SUN8I_DW_HDMI 54 63 tristate "Support for Allwinner version of DesignWare HDMI"
+2 -4
drivers/gpu/drm/sun4i/Makefile
··· 24 24 sun4i-tcon-y += sun4i_tcon.o 25 25 sun4i-tcon-y += sun4i_rgb.o 26 26 27 - sun6i-dsi-y += sun6i_mipi_dphy.o 28 - sun6i-dsi-y += sun6i_mipi_dsi.o 29 - 30 27 obj-$(CONFIG_DRM_SUN4I) += sun4i-drm.o 31 28 obj-$(CONFIG_DRM_SUN4I) += sun4i-tcon.o 32 29 obj-$(CONFIG_DRM_SUN4I) += sun4i_tv.o ··· 34 37 obj-$(CONFIG_DRM_SUN4I) += sun4i-frontend.o 35 38 endif 36 39 obj-$(CONFIG_DRM_SUN4I_HDMI) += sun4i-drm-hdmi.o 37 - obj-$(CONFIG_DRM_SUN6I_DSI) += sun6i-dsi.o 40 + obj-$(CONFIG_DRM_SUN6I_DPHY) += sun6i_mipi_dphy.o 41 + obj-$(CONFIG_DRM_SUN6I_DSI) += sun6i_mipi_dsi.o 38 42 obj-$(CONFIG_DRM_SUN8I_DW_HDMI) += sun8i-drm-hdmi.o 39 43 obj-$(CONFIG_DRM_SUN8I_MIXER) += sun8i-mixer.o 40 44 obj-$(CONFIG_DRM_SUN8I_TCON_TOP) += sun8i_tcon_top.o
+116 -90
drivers/gpu/drm/sun4i/sun6i_mipi_dphy.c
··· 8 8 9 9 #include <linux/bitops.h> 10 10 #include <linux/clk.h> 11 + #include <linux/module.h> 11 12 #include <linux/of_address.h> 13 + #include <linux/platform_device.h> 12 14 #include <linux/regmap.h> 13 15 #include <linux/reset.h> 14 16 15 - #include "sun6i_mipi_dsi.h" 17 + #include <linux/phy/phy.h> 18 + #include <linux/phy/phy-mipi-dphy.h> 16 19 17 20 #define SUN6I_DPHY_GCTL_REG 0x00 18 21 #define SUN6I_DPHY_GCTL_LANE_NUM(n) ((((n) - 1) & 3) << 4) ··· 84 81 85 82 #define SUN6I_DPHY_DBG5_REG 0xf4 86 83 87 - int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes) 84 + struct sun6i_dphy { 85 + struct clk *bus_clk; 86 + struct clk *mod_clk; 87 + struct regmap *regs; 88 + struct reset_control *reset; 89 + 90 + struct phy *phy; 91 + struct phy_configure_opts_mipi_dphy config; 92 + }; 93 + 94 + static int sun6i_dphy_init(struct phy *phy) 88 95 { 96 + struct sun6i_dphy *dphy = phy_get_drvdata(phy); 97 + 89 98 reset_control_deassert(dphy->reset); 90 99 clk_prepare_enable(dphy->mod_clk); 91 100 clk_set_rate_exclusive(dphy->mod_clk, 150000000); 101 + 102 + return 0; 103 + } 104 + 105 + static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts) 106 + { 107 + struct sun6i_dphy *dphy = phy_get_drvdata(phy); 108 + int ret; 109 + 110 + ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy); 111 + if (ret) 112 + return ret; 113 + 114 + memcpy(&dphy->config, opts, sizeof(dphy->config)); 115 + 116 + return 0; 117 + } 118 + 119 + static int sun6i_dphy_power_on(struct phy *phy) 120 + { 121 + struct sun6i_dphy *dphy = phy_get_drvdata(phy); 122 + u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0); 92 123 93 124 regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG, 94 125 SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT); ··· 148 111 SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3)); 149 112 150 113 regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG, 151 - SUN6I_DPHY_GCTL_LANE_NUM(lanes) | 114 + SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) | 152 115 SUN6I_DPHY_GCTL_EN); 153 - 154 - return 0; 155 - } 156 - 157 - int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes) 158 - { 159 - u8 lanes_mask = GENMASK(lanes - 1, 0); 160 116 161 117 regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, 162 118 SUN6I_DPHY_ANA0_REG_PWS | ··· 211 181 return 0; 212 182 } 213 183 214 - int sun6i_dphy_power_off(struct sun6i_dphy *dphy) 184 + static int sun6i_dphy_power_off(struct phy *phy) 215 185 { 186 + struct sun6i_dphy *dphy = phy_get_drvdata(phy); 187 + 216 188 regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG, 217 189 SUN6I_DPHY_ANA1_REG_VTTMODE, 0); 218 190 219 191 return 0; 220 192 } 221 193 222 - int sun6i_dphy_exit(struct sun6i_dphy *dphy) 194 + static int sun6i_dphy_exit(struct phy *phy) 223 195 { 196 + struct sun6i_dphy *dphy = phy_get_drvdata(phy); 197 + 224 198 clk_rate_exclusive_put(dphy->mod_clk); 225 199 clk_disable_unprepare(dphy->mod_clk); 226 200 reset_control_assert(dphy->reset); 227 201 228 202 return 0; 229 203 } 204 + 205 + 206 + static struct phy_ops sun6i_dphy_ops = { 207 + .configure = sun6i_dphy_configure, 208 + .power_on = sun6i_dphy_power_on, 209 + .power_off = sun6i_dphy_power_off, 210 + .init = sun6i_dphy_init, 211 + .exit = sun6i_dphy_exit, 212 + }; 230 213 231 214 static struct regmap_config sun6i_dphy_regmap_config = { 232 215 .reg_bits = 32, ··· 249 206 .name = "mipi-dphy", 250 207 }; 251 208 209 + static int sun6i_dphy_probe(struct platform_device *pdev) 210 + { 211 + struct phy_provider *phy_provider; 212 + struct sun6i_dphy *dphy; 213 + struct resource *res; 214 + void __iomem *regs; 215 + 216 + dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); 217 + if (!dphy) 218 + return -ENOMEM; 219 + 220 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 221 + regs = devm_ioremap_resource(&pdev->dev, res); 222 + if (IS_ERR(regs)) { 223 + dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n"); 224 + return PTR_ERR(regs); 225 + } 226 + 227 + dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus", 228 + regs, &sun6i_dphy_regmap_config); 229 + if (IS_ERR(dphy->regs)) { 230 + dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n"); 231 + return PTR_ERR(dphy->regs); 232 + } 233 + 234 + dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL); 235 + if (IS_ERR(dphy->reset)) { 236 + dev_err(&pdev->dev, "Couldn't get our reset line\n"); 237 + return PTR_ERR(dphy->reset); 238 + } 239 + 240 + dphy->mod_clk = devm_clk_get(&pdev->dev, "mod"); 241 + if (IS_ERR(dphy->mod_clk)) { 242 + dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n"); 243 + return PTR_ERR(dphy->mod_clk); 244 + } 245 + 246 + dphy->phy = devm_phy_create(&pdev->dev, NULL, &sun6i_dphy_ops); 247 + if (IS_ERR(dphy->phy)) { 248 + dev_err(&pdev->dev, "failed to create PHY\n"); 249 + return PTR_ERR(dphy->phy); 250 + } 251 + 252 + phy_set_drvdata(dphy->phy, dphy); 253 + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); 254 + 255 + return PTR_ERR_OR_ZERO(phy_provider); 256 + } 257 + 252 258 static const struct of_device_id sun6i_dphy_of_table[] = { 253 259 { .compatible = "allwinner,sun6i-a31-mipi-dphy" }, 254 260 { } 255 261 }; 262 + MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table); 256 263 257 - int sun6i_dphy_probe(struct sun6i_dsi *dsi, struct device_node *node) 258 - { 259 - struct sun6i_dphy *dphy; 260 - struct resource res; 261 - void __iomem *regs; 262 - int ret; 264 + static struct platform_driver sun6i_dphy_platform_driver = { 265 + .probe = sun6i_dphy_probe, 266 + .driver = { 267 + .name = "sun6i-mipi-dphy", 268 + .of_match_table = sun6i_dphy_of_table, 269 + }, 270 + }; 271 + module_platform_driver(sun6i_dphy_platform_driver); 263 272 264 - if (!of_match_node(sun6i_dphy_of_table, node)) { 265 - dev_err(dsi->dev, "Incompatible D-PHY\n"); 266 - return -EINVAL; 267 - } 268 - 269 - dphy = devm_kzalloc(dsi->dev, sizeof(*dphy), GFP_KERNEL); 270 - if (!dphy) 271 - return -ENOMEM; 272 - 273 - ret = of_address_to_resource(node, 0, &res); 274 - if (ret) { 275 - dev_err(dsi->dev, "phy: Couldn't get our resources\n"); 276 - return ret; 277 - } 278 - 279 - regs = devm_ioremap_resource(dsi->dev, &res); 280 - if (IS_ERR(regs)) { 281 - dev_err(dsi->dev, "Couldn't map the DPHY encoder registers\n"); 282 - return PTR_ERR(regs); 283 - } 284 - 285 - dphy->regs = devm_regmap_init_mmio(dsi->dev, regs, 286 - &sun6i_dphy_regmap_config); 287 - if (IS_ERR(dphy->regs)) { 288 - dev_err(dsi->dev, "Couldn't create the DPHY encoder regmap\n"); 289 - return PTR_ERR(dphy->regs); 290 - } 291 - 292 - dphy->reset = of_reset_control_get_shared(node, NULL); 293 - if (IS_ERR(dphy->reset)) { 294 - dev_err(dsi->dev, "Couldn't get our reset line\n"); 295 - return PTR_ERR(dphy->reset); 296 - } 297 - 298 - dphy->bus_clk = of_clk_get_by_name(node, "bus"); 299 - if (IS_ERR(dphy->bus_clk)) { 300 - dev_err(dsi->dev, "Couldn't get the DPHY bus clock\n"); 301 - ret = PTR_ERR(dphy->bus_clk); 302 - goto err_free_reset; 303 - } 304 - regmap_mmio_attach_clk(dphy->regs, dphy->bus_clk); 305 - 306 - dphy->mod_clk = of_clk_get_by_name(node, "mod"); 307 - if (IS_ERR(dphy->mod_clk)) { 308 - dev_err(dsi->dev, "Couldn't get the DPHY mod clock\n"); 309 - ret = PTR_ERR(dphy->mod_clk); 310 - goto err_free_bus; 311 - } 312 - 313 - dsi->dphy = dphy; 314 - 315 - return 0; 316 - 317 - err_free_bus: 318 - regmap_mmio_detach_clk(dphy->regs); 319 - clk_put(dphy->bus_clk); 320 - err_free_reset: 321 - reset_control_put(dphy->reset); 322 - return ret; 323 - } 324 - 325 - int sun6i_dphy_remove(struct sun6i_dsi *dsi) 326 - { 327 - struct sun6i_dphy *dphy = dsi->dphy; 328 - 329 - regmap_mmio_detach_clk(dphy->regs); 330 - clk_put(dphy->mod_clk); 331 - clk_put(dphy->bus_clk); 332 - reset_control_put(dphy->reset); 333 - 334 - return 0; 335 - } 273 + MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>"); 274 + MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver"); 275 + MODULE_LICENSE("GPL");
+18 -13
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.c
··· 16 16 #include <linux/slab.h> 17 17 18 18 #include <linux/phy/phy.h> 19 + #include <linux/phy/phy-mipi-dphy.h> 19 20 20 21 #include <drm/drmP.h> 21 22 #include <drm/drm_atomic_helper.h> ··· 617 616 struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; 618 617 struct sun6i_dsi *dsi = encoder_to_sun6i_dsi(encoder); 619 618 struct mipi_dsi_device *device = dsi->device; 619 + union phy_configure_opts opts = { 0 }; 620 + struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; 620 621 u16 delay; 621 622 622 623 DRM_DEBUG_DRIVER("Enabling DSI output\n"); ··· 637 634 sun6i_dsi_setup_format(dsi, mode); 638 635 sun6i_dsi_setup_timings(dsi, mode); 639 636 640 - sun6i_dphy_init(dsi->dphy, device->lanes); 641 - sun6i_dphy_power_on(dsi->dphy, device->lanes); 637 + phy_init(dsi->dphy); 638 + 639 + phy_mipi_dphy_get_default_config(mode->clock * 1000, 640 + mipi_dsi_pixel_format_to_bpp(device->format), 641 + device->lanes, cfg); 642 + 643 + phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY); 644 + phy_configure(dsi->dphy, &opts); 645 + phy_power_on(dsi->dphy); 642 646 643 647 if (!IS_ERR(dsi->panel)) 644 648 drm_panel_prepare(dsi->panel); ··· 683 673 drm_panel_unprepare(dsi->panel); 684 674 } 685 675 686 - sun6i_dphy_power_off(dsi->dphy); 687 - sun6i_dphy_exit(dsi->dphy); 676 + phy_power_off(dsi->dphy); 677 + phy_exit(dsi->dphy); 688 678 689 679 pm_runtime_put(dsi->dev); 690 680 } ··· 977 967 static int sun6i_dsi_probe(struct platform_device *pdev) 978 968 { 979 969 struct device *dev = &pdev->dev; 980 - struct device_node *dphy_node; 981 970 struct sun6i_dsi *dsi; 982 971 struct resource *res; 983 972 void __iomem *base; ··· 1022 1013 */ 1023 1014 clk_set_rate_exclusive(dsi->mod_clk, 297000000); 1024 1015 1025 - dphy_node = of_parse_phandle(dev->of_node, "phys", 0); 1026 - ret = sun6i_dphy_probe(dsi, dphy_node); 1027 - of_node_put(dphy_node); 1028 - if (ret) { 1016 + dsi->dphy = devm_phy_get(dev, "dphy"); 1017 + if (IS_ERR(dsi->dphy)) { 1029 1018 dev_err(dev, "Couldn't get the MIPI D-PHY\n"); 1030 1019 goto err_unprotect_clk; 1031 1020 } ··· 1033 1026 ret = mipi_dsi_host_register(&dsi->host); 1034 1027 if (ret) { 1035 1028 dev_err(dev, "Couldn't register MIPI-DSI host\n"); 1036 - goto err_remove_phy; 1029 + goto err_pm_disable; 1037 1030 } 1038 1031 1039 1032 ret = component_add(&pdev->dev, &sun6i_dsi_ops); ··· 1046 1039 1047 1040 err_remove_dsi_host: 1048 1041 mipi_dsi_host_unregister(&dsi->host); 1049 - err_remove_phy: 1042 + err_pm_disable: 1050 1043 pm_runtime_disable(dev); 1051 - sun6i_dphy_remove(dsi); 1052 1044 err_unprotect_clk: 1053 1045 clk_rate_exclusive_put(dsi->mod_clk); 1054 1046 return ret; ··· 1061 1055 component_del(&pdev->dev, &sun6i_dsi_ops); 1062 1056 mipi_dsi_host_unregister(&dsi->host); 1063 1057 pm_runtime_disable(dev); 1064 - sun6i_dphy_remove(dsi); 1065 1058 clk_rate_exclusive_put(dsi->mod_clk); 1066 1059 1067 1060 return 0;
+1 -16
drivers/gpu/drm/sun4i/sun6i_mipi_dsi.h
··· 13 13 #include <drm/drm_encoder.h> 14 14 #include <drm/drm_mipi_dsi.h> 15 15 16 - struct sun6i_dphy { 17 - struct clk *bus_clk; 18 - struct clk *mod_clk; 19 - struct regmap *regs; 20 - struct reset_control *reset; 21 - }; 22 - 23 16 struct sun6i_dsi { 24 17 struct drm_connector connector; 25 18 struct drm_encoder encoder; ··· 22 29 struct clk *mod_clk; 23 30 struct regmap *regs; 24 31 struct reset_control *reset; 25 - struct sun6i_dphy *dphy; 32 + struct phy *dphy; 26 33 27 34 struct device *dev; 28 35 struct sun4i_drv *drv; ··· 44 51 { 45 52 return container_of(encoder, struct sun6i_dsi, encoder); 46 53 }; 47 - 48 - int sun6i_dphy_probe(struct sun6i_dsi *dsi, struct device_node *node); 49 - int sun6i_dphy_remove(struct sun6i_dsi *dsi); 50 - 51 - int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes); 52 - int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes); 53 - int sun6i_dphy_power_off(struct sun6i_dphy *dphy); 54 - int sun6i_dphy_exit(struct sun6i_dphy *dphy); 55 54 56 55 #endif /* _SUN6I_MIPI_DSI_H_ */