1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (c) 2016 Allwinnertech Co., Ltd. 4 * Copyright (C) 2017-2018 Bootlin 5 * 6 * Maxime Ripard <maxime.ripard@free-electrons.com> 7 */ 8 9 #include <linux/bitops.h> 10 #include <linux/clk.h> 11 #include <linux/module.h> 12 #include <linux/of_address.h> 13 #include <linux/platform_device.h> 14 #include <linux/regmap.h> 15 #include <linux/reset.h> 16 17 #include <linux/phy/phy.h> 18 #include <linux/phy/phy-mipi-dphy.h> 19 20 #define SUN6I_DPHY_GCTL_REG 0x00 21 #define SUN6I_DPHY_GCTL_LANE_NUM(n) ((((n) - 1) & 3) << 4) 22 #define SUN6I_DPHY_GCTL_EN BIT(0) 23 24 #define SUN6I_DPHY_TX_CTL_REG 0x04 25 #define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT BIT(28) 26 27 #define SUN6I_DPHY_RX_CTL_REG 0x08 28 #define SUN6I_DPHY_RX_CTL_EN_DBC BIT(31) 29 #define SUN6I_DPHY_RX_CTL_RX_CLK_FORCE BIT(24) 30 #define SUN6I_DPHY_RX_CTL_RX_D3_FORCE BIT(23) 31 #define SUN6I_DPHY_RX_CTL_RX_D2_FORCE BIT(22) 32 #define SUN6I_DPHY_RX_CTL_RX_D1_FORCE BIT(21) 33 #define SUN6I_DPHY_RX_CTL_RX_D0_FORCE BIT(20) 34 35 #define SUN6I_DPHY_TX_TIME0_REG 0x10 36 #define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n) (((n) & 0xff) << 24) 37 #define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n) (((n) & 0xff) << 16) 38 #define SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(n) ((n) & 0xff) 39 40 #define SUN6I_DPHY_TX_TIME1_REG 0x14 41 #define SUN6I_DPHY_TX_TIME1_CLK_POST(n) (((n) & 0xff) << 24) 42 #define SUN6I_DPHY_TX_TIME1_CLK_PRE(n) (((n) & 0xff) << 16) 43 #define SUN6I_DPHY_TX_TIME1_CLK_ZERO(n) (((n) & 0xff) << 8) 44 #define SUN6I_DPHY_TX_TIME1_CLK_PREPARE(n) ((n) & 0xff) 45 46 #define SUN6I_DPHY_TX_TIME2_REG 0x18 47 #define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n) ((n) & 0xff) 48 49 #define SUN6I_DPHY_TX_TIME3_REG 0x1c 50 51 #define SUN6I_DPHY_TX_TIME4_REG 0x20 52 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n) (((n) & 0xff) << 8) 53 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n) ((n) & 0xff) 54 55 #define SUN6I_DPHY_RX_TIME0_REG 0x30 56 #define SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(n) (((n) & 0xff) << 24) 57 #define SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(n) (((n) & 0xff) << 16) 58 #define SUN6I_DPHY_RX_TIME0_LP_RX(n) (((n) & 0xff) << 8) 59 60 #define SUN6I_DPHY_RX_TIME1_REG 0x34 61 #define SUN6I_DPHY_RX_TIME1_RX_DLY(n) (((n) & 0xfff) << 20) 62 #define SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(n) ((n) & 0xfffff) 63 64 #define SUN6I_DPHY_RX_TIME2_REG 0x38 65 #define SUN6I_DPHY_RX_TIME2_HS_RX_ANA1(n) (((n) & 0xff) << 8) 66 #define SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(n) ((n) & 0xff) 67 68 #define SUN6I_DPHY_RX_TIME3_REG 0x40 69 #define SUN6I_DPHY_RX_TIME3_LPRST_DLY(n) (((n) & 0xffff) << 16) 70 71 #define SUN6I_DPHY_ANA0_REG 0x4c 72 #define SUN6I_DPHY_ANA0_REG_PWS BIT(31) 73 #define SUN6I_DPHY_ANA0_REG_DMPC BIT(28) 74 #define SUN6I_DPHY_ANA0_REG_DMPD(n) (((n) & 0xf) << 24) 75 #define SUN6I_DPHY_ANA0_REG_SLV(n) (((n) & 7) << 12) 76 #define SUN6I_DPHY_ANA0_REG_DEN(n) (((n) & 0xf) << 8) 77 #define SUN6I_DPHY_ANA0_REG_SFB(n) (((n) & 3) << 2) 78 79 #define SUN6I_DPHY_ANA1_REG 0x50 80 #define SUN6I_DPHY_ANA1_REG_VTTMODE BIT(31) 81 #define SUN6I_DPHY_ANA1_REG_CSMPS(n) (((n) & 3) << 28) 82 #define SUN6I_DPHY_ANA1_REG_SVTT(n) (((n) & 0xf) << 24) 83 84 #define SUN6I_DPHY_ANA2_REG 0x54 85 #define SUN6I_DPHY_ANA2_EN_P2S_CPU(n) (((n) & 0xf) << 24) 86 #define SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK GENMASK(27, 24) 87 #define SUN6I_DPHY_ANA2_EN_CK_CPU BIT(4) 88 #define SUN6I_DPHY_ANA2_REG_ENIB BIT(1) 89 90 #define SUN6I_DPHY_ANA3_REG 0x58 91 #define SUN6I_DPHY_ANA3_EN_VTTD(n) (((n) & 0xf) << 28) 92 #define SUN6I_DPHY_ANA3_EN_VTTD_MASK GENMASK(31, 28) 93 #define SUN6I_DPHY_ANA3_EN_VTTC BIT(27) 94 #define SUN6I_DPHY_ANA3_EN_DIV BIT(26) 95 #define SUN6I_DPHY_ANA3_EN_LDOC BIT(25) 96 #define SUN6I_DPHY_ANA3_EN_LDOD BIT(24) 97 #define SUN6I_DPHY_ANA3_EN_LDOR BIT(18) 98 99 #define SUN6I_DPHY_ANA4_REG 0x5c 100 #define SUN6I_DPHY_ANA4_REG_DMPLVC BIT(24) 101 #define SUN6I_DPHY_ANA4_REG_DMPLVD(n) (((n) & 0xf) << 20) 102 #define SUN6I_DPHY_ANA4_REG_CKDV(n) (((n) & 0x1f) << 12) 103 #define SUN6I_DPHY_ANA4_REG_TMSC(n) (((n) & 3) << 10) 104 #define SUN6I_DPHY_ANA4_REG_TMSD(n) (((n) & 3) << 8) 105 #define SUN6I_DPHY_ANA4_REG_TXDNSC(n) (((n) & 3) << 6) 106 #define SUN6I_DPHY_ANA4_REG_TXDNSD(n) (((n) & 3) << 4) 107 #define SUN6I_DPHY_ANA4_REG_TXPUSC(n) (((n) & 3) << 2) 108 #define SUN6I_DPHY_ANA4_REG_TXPUSD(n) ((n) & 3) 109 110 #define SUN6I_DPHY_DBG5_REG 0xf4 111 112 enum sun6i_dphy_direction { 113 SUN6I_DPHY_DIRECTION_TX, 114 SUN6I_DPHY_DIRECTION_RX, 115 }; 116 117 struct sun6i_dphy; 118 119 struct sun6i_dphy_variant { 120 void (*tx_power_on)(struct sun6i_dphy *dphy); 121 bool rx_supported; 122 }; 123 124 struct sun6i_dphy { 125 struct clk *bus_clk; 126 struct clk *mod_clk; 127 struct regmap *regs; 128 struct reset_control *reset; 129 130 struct phy *phy; 131 struct phy_configure_opts_mipi_dphy config; 132 133 const struct sun6i_dphy_variant *variant; 134 enum sun6i_dphy_direction direction; 135 }; 136 137 static int sun6i_dphy_init(struct phy *phy) 138 { 139 struct sun6i_dphy *dphy = phy_get_drvdata(phy); 140 141 reset_control_deassert(dphy->reset); 142 clk_prepare_enable(dphy->mod_clk); 143 clk_set_rate_exclusive(dphy->mod_clk, 150000000); 144 145 return 0; 146 } 147 148 static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts) 149 { 150 struct sun6i_dphy *dphy = phy_get_drvdata(phy); 151 int ret; 152 153 ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy); 154 if (ret) 155 return ret; 156 157 memcpy(&dphy->config, opts, sizeof(dphy->config)); 158 159 return 0; 160 } 161 162 static void sun6i_a31_mipi_dphy_tx_power_on(struct sun6i_dphy *dphy) 163 { 164 u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0); 165 166 regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, 167 SUN6I_DPHY_ANA0_REG_PWS | 168 SUN6I_DPHY_ANA0_REG_DMPC | 169 SUN6I_DPHY_ANA0_REG_SLV(7) | 170 SUN6I_DPHY_ANA0_REG_DMPD(lanes_mask) | 171 SUN6I_DPHY_ANA0_REG_DEN(lanes_mask)); 172 173 regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG, 174 SUN6I_DPHY_ANA1_REG_CSMPS(1) | 175 SUN6I_DPHY_ANA1_REG_SVTT(7)); 176 177 regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG, 178 SUN6I_DPHY_ANA4_REG_CKDV(1) | 179 SUN6I_DPHY_ANA4_REG_TMSC(1) | 180 SUN6I_DPHY_ANA4_REG_TMSD(1) | 181 SUN6I_DPHY_ANA4_REG_TXDNSC(1) | 182 SUN6I_DPHY_ANA4_REG_TXDNSD(1) | 183 SUN6I_DPHY_ANA4_REG_TXPUSC(1) | 184 SUN6I_DPHY_ANA4_REG_TXPUSD(1) | 185 SUN6I_DPHY_ANA4_REG_DMPLVC | 186 SUN6I_DPHY_ANA4_REG_DMPLVD(lanes_mask)); 187 188 regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG, 189 SUN6I_DPHY_ANA2_REG_ENIB); 190 udelay(5); 191 192 regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG, 193 SUN6I_DPHY_ANA3_EN_LDOR | 194 SUN6I_DPHY_ANA3_EN_LDOC | 195 SUN6I_DPHY_ANA3_EN_LDOD); 196 udelay(1); 197 } 198 199 static int sun6i_dphy_tx_power_on(struct sun6i_dphy *dphy) 200 { 201 u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0); 202 203 regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG, 204 SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT); 205 206 regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME0_REG, 207 SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) | 208 SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) | 209 SUN6I_DPHY_TX_TIME0_HS_TRAIL(10)); 210 211 regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME1_REG, 212 SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) | 213 SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) | 214 SUN6I_DPHY_TX_TIME1_CLK_PRE(3) | 215 SUN6I_DPHY_TX_TIME1_CLK_POST(10)); 216 217 regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME2_REG, 218 SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30)); 219 220 regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME3_REG, 0); 221 222 regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME4_REG, 223 SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) | 224 SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3)); 225 226 dphy->variant->tx_power_on(dphy); 227 228 regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG, 229 SUN6I_DPHY_ANA3_EN_VTTC | 230 SUN6I_DPHY_ANA3_EN_VTTD_MASK, 231 SUN6I_DPHY_ANA3_EN_VTTC | 232 SUN6I_DPHY_ANA3_EN_VTTD(lanes_mask)); 233 udelay(1); 234 235 regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG, 236 SUN6I_DPHY_ANA3_EN_DIV, 237 SUN6I_DPHY_ANA3_EN_DIV); 238 udelay(1); 239 240 regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG, 241 SUN6I_DPHY_ANA2_EN_CK_CPU, 242 SUN6I_DPHY_ANA2_EN_CK_CPU); 243 udelay(1); 244 245 regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG, 246 SUN6I_DPHY_ANA1_REG_VTTMODE, 247 SUN6I_DPHY_ANA1_REG_VTTMODE); 248 249 regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG, 250 SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK, 251 SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask)); 252 253 regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG, 254 SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) | 255 SUN6I_DPHY_GCTL_EN); 256 257 return 0; 258 } 259 260 static int sun6i_dphy_rx_power_on(struct sun6i_dphy *dphy) 261 { 262 /* Physical clock rate is actually half of symbol rate with DDR. */ 263 unsigned long mipi_symbol_rate = dphy->config.hs_clk_rate; 264 unsigned long dphy_clk_rate; 265 unsigned int rx_dly; 266 unsigned int lprst_dly; 267 u32 value; 268 269 dphy_clk_rate = clk_get_rate(dphy->mod_clk); 270 if (!dphy_clk_rate) 271 return -EINVAL; 272 273 /* Hardcoded timing parameters from the Allwinner BSP. */ 274 regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME0_REG, 275 SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(255) | 276 SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(255) | 277 SUN6I_DPHY_RX_TIME0_LP_RX(255)); 278 279 /* 280 * Formula from the Allwinner BSP, with hardcoded coefficients 281 * (probably internal divider/multiplier). 282 */ 283 rx_dly = 8 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 8)); 284 285 /* 286 * The Allwinner BSP has an alternative formula for LP_RX_ULPS_WP: 287 * lp_ulps_wp_cnt = lp_ulps_wp_ms * lp_clk / 1000 288 * but does not use it and hardcodes 255 instead. 289 */ 290 regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME1_REG, 291 SUN6I_DPHY_RX_TIME1_RX_DLY(rx_dly) | 292 SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(255)); 293 294 /* HS_RX_ANA0 value is hardcoded in the Allwinner BSP. */ 295 regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME2_REG, 296 SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(4)); 297 298 /* 299 * Formula from the Allwinner BSP, with hardcoded coefficients 300 * (probably internal divider/multiplier). 301 */ 302 lprst_dly = 4 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 2)); 303 304 regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME3_REG, 305 SUN6I_DPHY_RX_TIME3_LPRST_DLY(lprst_dly)); 306 307 /* Analog parameters are hardcoded in the Allwinner BSP. */ 308 regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, 309 SUN6I_DPHY_ANA0_REG_PWS | 310 SUN6I_DPHY_ANA0_REG_SLV(7) | 311 SUN6I_DPHY_ANA0_REG_SFB(2)); 312 313 regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG, 314 SUN6I_DPHY_ANA1_REG_SVTT(4)); 315 316 regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG, 317 SUN6I_DPHY_ANA4_REG_DMPLVC | 318 SUN6I_DPHY_ANA4_REG_DMPLVD(1)); 319 320 regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG, 321 SUN6I_DPHY_ANA2_REG_ENIB); 322 323 regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG, 324 SUN6I_DPHY_ANA3_EN_LDOR | 325 SUN6I_DPHY_ANA3_EN_LDOC | 326 SUN6I_DPHY_ANA3_EN_LDOD); 327 328 /* 329 * Delay comes from the Allwinner BSP, likely for internal regulator 330 * ramp-up. 331 */ 332 udelay(3); 333 334 value = SUN6I_DPHY_RX_CTL_EN_DBC | SUN6I_DPHY_RX_CTL_RX_CLK_FORCE; 335 336 /* 337 * Rx data lane force-enable bits are used as regular RX enable by the 338 * Allwinner BSP. 339 */ 340 if (dphy->config.lanes >= 1) 341 value |= SUN6I_DPHY_RX_CTL_RX_D0_FORCE; 342 if (dphy->config.lanes >= 2) 343 value |= SUN6I_DPHY_RX_CTL_RX_D1_FORCE; 344 if (dphy->config.lanes >= 3) 345 value |= SUN6I_DPHY_RX_CTL_RX_D2_FORCE; 346 if (dphy->config.lanes == 4) 347 value |= SUN6I_DPHY_RX_CTL_RX_D3_FORCE; 348 349 regmap_write(dphy->regs, SUN6I_DPHY_RX_CTL_REG, value); 350 351 regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG, 352 SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) | 353 SUN6I_DPHY_GCTL_EN); 354 355 return 0; 356 } 357 358 static int sun6i_dphy_power_on(struct phy *phy) 359 { 360 struct sun6i_dphy *dphy = phy_get_drvdata(phy); 361 362 switch (dphy->direction) { 363 case SUN6I_DPHY_DIRECTION_TX: 364 return sun6i_dphy_tx_power_on(dphy); 365 case SUN6I_DPHY_DIRECTION_RX: 366 return sun6i_dphy_rx_power_on(dphy); 367 default: 368 return -EINVAL; 369 } 370 } 371 372 static int sun6i_dphy_power_off(struct phy *phy) 373 { 374 struct sun6i_dphy *dphy = phy_get_drvdata(phy); 375 376 regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG, 0); 377 378 regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, 0); 379 regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG, 0); 380 regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG, 0); 381 regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG, 0); 382 regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG, 0); 383 384 return 0; 385 } 386 387 static int sun6i_dphy_exit(struct phy *phy) 388 { 389 struct sun6i_dphy *dphy = phy_get_drvdata(phy); 390 391 clk_rate_exclusive_put(dphy->mod_clk); 392 clk_disable_unprepare(dphy->mod_clk); 393 reset_control_assert(dphy->reset); 394 395 return 0; 396 } 397 398 399 static const struct phy_ops sun6i_dphy_ops = { 400 .configure = sun6i_dphy_configure, 401 .power_on = sun6i_dphy_power_on, 402 .power_off = sun6i_dphy_power_off, 403 .init = sun6i_dphy_init, 404 .exit = sun6i_dphy_exit, 405 }; 406 407 static const struct regmap_config sun6i_dphy_regmap_config = { 408 .reg_bits = 32, 409 .val_bits = 32, 410 .reg_stride = 4, 411 .max_register = SUN6I_DPHY_DBG5_REG, 412 .name = "mipi-dphy", 413 }; 414 415 static int sun6i_dphy_probe(struct platform_device *pdev) 416 { 417 struct phy_provider *phy_provider; 418 struct sun6i_dphy *dphy; 419 const char *direction; 420 void __iomem *regs; 421 int ret; 422 423 dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); 424 if (!dphy) 425 return -ENOMEM; 426 427 dphy->variant = device_get_match_data(&pdev->dev); 428 if (!dphy->variant) 429 return -EINVAL; 430 431 regs = devm_platform_ioremap_resource(pdev, 0); 432 if (IS_ERR(regs)) { 433 dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n"); 434 return PTR_ERR(regs); 435 } 436 437 dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus", 438 regs, &sun6i_dphy_regmap_config); 439 if (IS_ERR(dphy->regs)) { 440 dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n"); 441 return PTR_ERR(dphy->regs); 442 } 443 444 dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL); 445 if (IS_ERR(dphy->reset)) { 446 dev_err(&pdev->dev, "Couldn't get our reset line\n"); 447 return PTR_ERR(dphy->reset); 448 } 449 450 dphy->mod_clk = devm_clk_get(&pdev->dev, "mod"); 451 if (IS_ERR(dphy->mod_clk)) { 452 dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n"); 453 return PTR_ERR(dphy->mod_clk); 454 } 455 456 dphy->phy = devm_phy_create(&pdev->dev, NULL, &sun6i_dphy_ops); 457 if (IS_ERR(dphy->phy)) { 458 dev_err(&pdev->dev, "failed to create PHY\n"); 459 return PTR_ERR(dphy->phy); 460 } 461 462 dphy->direction = SUN6I_DPHY_DIRECTION_TX; 463 464 ret = of_property_read_string(pdev->dev.of_node, "allwinner,direction", 465 &direction); 466 467 if (!ret && !strncmp(direction, "rx", 2)) { 468 if (!dphy->variant->rx_supported) { 469 dev_err(&pdev->dev, "RX not supported on this variant\n"); 470 return -EOPNOTSUPP; 471 } 472 473 dphy->direction = SUN6I_DPHY_DIRECTION_RX; 474 } 475 476 phy_set_drvdata(dphy->phy, dphy); 477 phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); 478 479 return PTR_ERR_OR_ZERO(phy_provider); 480 } 481 482 static const struct sun6i_dphy_variant sun6i_a31_mipi_dphy_variant = { 483 .tx_power_on = sun6i_a31_mipi_dphy_tx_power_on, 484 .rx_supported = true, 485 }; 486 487 static const struct of_device_id sun6i_dphy_of_table[] = { 488 { 489 .compatible = "allwinner,sun6i-a31-mipi-dphy", 490 .data = &sun6i_a31_mipi_dphy_variant, 491 }, 492 { } 493 }; 494 MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table); 495 496 static struct platform_driver sun6i_dphy_platform_driver = { 497 .probe = sun6i_dphy_probe, 498 .driver = { 499 .name = "sun6i-mipi-dphy", 500 .of_match_table = sun6i_dphy_of_table, 501 }, 502 }; 503 module_platform_driver(sun6i_dphy_platform_driver); 504 505 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>"); 506 MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver"); 507 MODULE_LICENSE("GPL"); 508