Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Copyright (c) 2016 Allwinnertech Co., Ltd.
0004  * Copyright (C) 2017-2018 Bootlin
0005  *
0006  * Maxime Ripard <maxime.ripard@free-electrons.com>
0007  */
0008 
0009 #include <linux/bitops.h>
0010 #include <linux/clk.h>
0011 #include <linux/module.h>
0012 #include <linux/of_address.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/regmap.h>
0015 #include <linux/reset.h>
0016 
0017 #include <linux/phy/phy.h>
0018 #include <linux/phy/phy-mipi-dphy.h>
0019 
0020 #define SUN6I_DPHY_GCTL_REG     0x00
0021 #define SUN6I_DPHY_GCTL_LANE_NUM(n)     ((((n) - 1) & 3) << 4)
0022 #define SUN6I_DPHY_GCTL_EN          BIT(0)
0023 
0024 #define SUN6I_DPHY_TX_CTL_REG       0x04
0025 #define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT    BIT(28)
0026 
0027 #define SUN6I_DPHY_RX_CTL_REG       0x08
0028 #define SUN6I_DPHY_RX_CTL_EN_DBC    BIT(31)
0029 #define SUN6I_DPHY_RX_CTL_RX_CLK_FORCE  BIT(24)
0030 #define SUN6I_DPHY_RX_CTL_RX_D3_FORCE   BIT(23)
0031 #define SUN6I_DPHY_RX_CTL_RX_D2_FORCE   BIT(22)
0032 #define SUN6I_DPHY_RX_CTL_RX_D1_FORCE   BIT(21)
0033 #define SUN6I_DPHY_RX_CTL_RX_D0_FORCE   BIT(20)
0034 
0035 #define SUN6I_DPHY_TX_TIME0_REG     0x10
0036 #define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n)     (((n) & 0xff) << 24)
0037 #define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n)   (((n) & 0xff) << 16)
0038 #define SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(n)   ((n) & 0xff)
0039 
0040 #define SUN6I_DPHY_TX_TIME1_REG     0x14
0041 #define SUN6I_DPHY_TX_TIME1_CLK_POST(n)     (((n) & 0xff) << 24)
0042 #define SUN6I_DPHY_TX_TIME1_CLK_PRE(n)      (((n) & 0xff) << 16)
0043 #define SUN6I_DPHY_TX_TIME1_CLK_ZERO(n)     (((n) & 0xff) << 8)
0044 #define SUN6I_DPHY_TX_TIME1_CLK_PREPARE(n)  ((n) & 0xff)
0045 
0046 #define SUN6I_DPHY_TX_TIME2_REG     0x18
0047 #define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n)    ((n) & 0xff)
0048 
0049 #define SUN6I_DPHY_TX_TIME3_REG     0x1c
0050 
0051 #define SUN6I_DPHY_TX_TIME4_REG     0x20
0052 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n)   (((n) & 0xff) << 8)
0053 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n)   ((n) & 0xff)
0054 
0055 #define SUN6I_DPHY_RX_TIME0_REG     0x30
0056 #define SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(n)   (((n) & 0xff) << 24)
0057 #define SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(n)   (((n) & 0xff) << 16)
0058 #define SUN6I_DPHY_RX_TIME0_LP_RX(n)        (((n) & 0xff) << 8)
0059 
0060 #define SUN6I_DPHY_RX_TIME1_REG     0x34
0061 #define SUN6I_DPHY_RX_TIME1_RX_DLY(n)       (((n) & 0xfff) << 20)
0062 #define SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(n)    ((n) & 0xfffff)
0063 
0064 #define SUN6I_DPHY_RX_TIME2_REG     0x38
0065 #define SUN6I_DPHY_RX_TIME2_HS_RX_ANA1(n)   (((n) & 0xff) << 8)
0066 #define SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(n)   ((n) & 0xff)
0067 
0068 #define SUN6I_DPHY_RX_TIME3_REG     0x40
0069 #define SUN6I_DPHY_RX_TIME3_LPRST_DLY(n)    (((n) & 0xffff) << 16)
0070 
0071 #define SUN6I_DPHY_ANA0_REG     0x4c
0072 #define SUN6I_DPHY_ANA0_REG_PWS         BIT(31)
0073 #define SUN6I_DPHY_ANA0_REG_DMPC        BIT(28)
0074 #define SUN6I_DPHY_ANA0_REG_DMPD(n)     (((n) & 0xf) << 24)
0075 #define SUN6I_DPHY_ANA0_REG_SLV(n)      (((n) & 7) << 12)
0076 #define SUN6I_DPHY_ANA0_REG_DEN(n)      (((n) & 0xf) << 8)
0077 #define SUN6I_DPHY_ANA0_REG_SFB(n)      (((n) & 3) << 2)
0078 
0079 #define SUN6I_DPHY_ANA1_REG     0x50
0080 #define SUN6I_DPHY_ANA1_REG_VTTMODE     BIT(31)
0081 #define SUN6I_DPHY_ANA1_REG_CSMPS(n)        (((n) & 3) << 28)
0082 #define SUN6I_DPHY_ANA1_REG_SVTT(n)     (((n) & 0xf) << 24)
0083 
0084 #define SUN6I_DPHY_ANA2_REG     0x54
0085 #define SUN6I_DPHY_ANA2_EN_P2S_CPU(n)       (((n) & 0xf) << 24)
0086 #define SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK     GENMASK(27, 24)
0087 #define SUN6I_DPHY_ANA2_EN_CK_CPU       BIT(4)
0088 #define SUN6I_DPHY_ANA2_REG_ENIB        BIT(1)
0089 
0090 #define SUN6I_DPHY_ANA3_REG     0x58
0091 #define SUN6I_DPHY_ANA3_EN_VTTD(n)      (((n) & 0xf) << 28)
0092 #define SUN6I_DPHY_ANA3_EN_VTTD_MASK        GENMASK(31, 28)
0093 #define SUN6I_DPHY_ANA3_EN_VTTC         BIT(27)
0094 #define SUN6I_DPHY_ANA3_EN_DIV          BIT(26)
0095 #define SUN6I_DPHY_ANA3_EN_LDOC         BIT(25)
0096 #define SUN6I_DPHY_ANA3_EN_LDOD         BIT(24)
0097 #define SUN6I_DPHY_ANA3_EN_LDOR         BIT(18)
0098 
0099 #define SUN6I_DPHY_ANA4_REG     0x5c
0100 #define SUN6I_DPHY_ANA4_REG_DMPLVC      BIT(24)
0101 #define SUN6I_DPHY_ANA4_REG_DMPLVD(n)       (((n) & 0xf) << 20)
0102 #define SUN6I_DPHY_ANA4_REG_CKDV(n)     (((n) & 0x1f) << 12)
0103 #define SUN6I_DPHY_ANA4_REG_TMSC(n)     (((n) & 3) << 10)
0104 #define SUN6I_DPHY_ANA4_REG_TMSD(n)     (((n) & 3) << 8)
0105 #define SUN6I_DPHY_ANA4_REG_TXDNSC(n)       (((n) & 3) << 6)
0106 #define SUN6I_DPHY_ANA4_REG_TXDNSD(n)       (((n) & 3) << 4)
0107 #define SUN6I_DPHY_ANA4_REG_TXPUSC(n)       (((n) & 3) << 2)
0108 #define SUN6I_DPHY_ANA4_REG_TXPUSD(n)       ((n) & 3)
0109 
0110 #define SUN6I_DPHY_DBG5_REG     0xf4
0111 
0112 enum sun6i_dphy_direction {
0113     SUN6I_DPHY_DIRECTION_TX,
0114     SUN6I_DPHY_DIRECTION_RX,
0115 };
0116 
0117 struct sun6i_dphy {
0118     struct clk              *bus_clk;
0119     struct clk              *mod_clk;
0120     struct regmap               *regs;
0121     struct reset_control            *reset;
0122 
0123     struct phy              *phy;
0124     struct phy_configure_opts_mipi_dphy config;
0125 
0126     enum sun6i_dphy_direction       direction;
0127 };
0128 
0129 static int sun6i_dphy_init(struct phy *phy)
0130 {
0131     struct sun6i_dphy *dphy = phy_get_drvdata(phy);
0132 
0133     reset_control_deassert(dphy->reset);
0134     clk_prepare_enable(dphy->mod_clk);
0135     clk_set_rate_exclusive(dphy->mod_clk, 150000000);
0136 
0137     return 0;
0138 }
0139 
0140 static int sun6i_dphy_configure(struct phy *phy, union phy_configure_opts *opts)
0141 {
0142     struct sun6i_dphy *dphy = phy_get_drvdata(phy);
0143     int ret;
0144 
0145     ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
0146     if (ret)
0147         return ret;
0148 
0149     memcpy(&dphy->config, opts, sizeof(dphy->config));
0150 
0151     return 0;
0152 }
0153 
0154 static int sun6i_dphy_tx_power_on(struct sun6i_dphy *dphy)
0155 {
0156     u8 lanes_mask = GENMASK(dphy->config.lanes - 1, 0);
0157 
0158     regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
0159              SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);
0160 
0161     regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME0_REG,
0162              SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) |
0163              SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) |
0164              SUN6I_DPHY_TX_TIME0_HS_TRAIL(10));
0165 
0166     regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME1_REG,
0167              SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) |
0168              SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) |
0169              SUN6I_DPHY_TX_TIME1_CLK_PRE(3) |
0170              SUN6I_DPHY_TX_TIME1_CLK_POST(10));
0171 
0172     regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME2_REG,
0173              SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30));
0174 
0175     regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME3_REG, 0);
0176 
0177     regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME4_REG,
0178              SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) |
0179              SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
0180 
0181     regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
0182              SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
0183              SUN6I_DPHY_GCTL_EN);
0184 
0185     regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
0186              SUN6I_DPHY_ANA0_REG_PWS |
0187              SUN6I_DPHY_ANA0_REG_DMPC |
0188              SUN6I_DPHY_ANA0_REG_SLV(7) |
0189              SUN6I_DPHY_ANA0_REG_DMPD(lanes_mask) |
0190              SUN6I_DPHY_ANA0_REG_DEN(lanes_mask));
0191 
0192     regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
0193              SUN6I_DPHY_ANA1_REG_CSMPS(1) |
0194              SUN6I_DPHY_ANA1_REG_SVTT(7));
0195 
0196     regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
0197              SUN6I_DPHY_ANA4_REG_CKDV(1) |
0198              SUN6I_DPHY_ANA4_REG_TMSC(1) |
0199              SUN6I_DPHY_ANA4_REG_TMSD(1) |
0200              SUN6I_DPHY_ANA4_REG_TXDNSC(1) |
0201              SUN6I_DPHY_ANA4_REG_TXDNSD(1) |
0202              SUN6I_DPHY_ANA4_REG_TXPUSC(1) |
0203              SUN6I_DPHY_ANA4_REG_TXPUSD(1) |
0204              SUN6I_DPHY_ANA4_REG_DMPLVC |
0205              SUN6I_DPHY_ANA4_REG_DMPLVD(lanes_mask));
0206 
0207     regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
0208              SUN6I_DPHY_ANA2_REG_ENIB);
0209     udelay(5);
0210 
0211     regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
0212              SUN6I_DPHY_ANA3_EN_LDOR |
0213              SUN6I_DPHY_ANA3_EN_LDOC |
0214              SUN6I_DPHY_ANA3_EN_LDOD);
0215     udelay(1);
0216 
0217     regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
0218                SUN6I_DPHY_ANA3_EN_VTTC |
0219                SUN6I_DPHY_ANA3_EN_VTTD_MASK,
0220                SUN6I_DPHY_ANA3_EN_VTTC |
0221                SUN6I_DPHY_ANA3_EN_VTTD(lanes_mask));
0222     udelay(1);
0223 
0224     regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
0225                SUN6I_DPHY_ANA3_EN_DIV,
0226                SUN6I_DPHY_ANA3_EN_DIV);
0227     udelay(1);
0228 
0229     regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
0230                SUN6I_DPHY_ANA2_EN_CK_CPU,
0231                SUN6I_DPHY_ANA2_EN_CK_CPU);
0232     udelay(1);
0233 
0234     regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
0235                SUN6I_DPHY_ANA1_REG_VTTMODE,
0236                SUN6I_DPHY_ANA1_REG_VTTMODE);
0237 
0238     regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
0239                SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK,
0240                SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask));
0241 
0242     return 0;
0243 }
0244 
0245 static int sun6i_dphy_rx_power_on(struct sun6i_dphy *dphy)
0246 {
0247     /* Physical clock rate is actually half of symbol rate with DDR. */
0248     unsigned long mipi_symbol_rate = dphy->config.hs_clk_rate;
0249     unsigned long dphy_clk_rate;
0250     unsigned int rx_dly;
0251     unsigned int lprst_dly;
0252     u32 value;
0253 
0254     dphy_clk_rate = clk_get_rate(dphy->mod_clk);
0255     if (!dphy_clk_rate)
0256         return -EINVAL;
0257 
0258     /* Hardcoded timing parameters from the Allwinner BSP. */
0259     regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME0_REG,
0260              SUN6I_DPHY_RX_TIME0_HS_RX_SYNC(255) |
0261              SUN6I_DPHY_RX_TIME0_HS_RX_CLK_MISS(255) |
0262              SUN6I_DPHY_RX_TIME0_LP_RX(255));
0263 
0264     /*
0265      * Formula from the Allwinner BSP, with hardcoded coefficients
0266      * (probably internal divider/multiplier).
0267      */
0268     rx_dly = 8 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 8));
0269 
0270     /*
0271      * The Allwinner BSP has an alternative formula for LP_RX_ULPS_WP:
0272      * lp_ulps_wp_cnt = lp_ulps_wp_ms * lp_clk / 1000
0273      * but does not use it and hardcodes 255 instead.
0274      */
0275     regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME1_REG,
0276              SUN6I_DPHY_RX_TIME1_RX_DLY(rx_dly) |
0277              SUN6I_DPHY_RX_TIME1_LP_RX_ULPS_WP(255));
0278 
0279     /* HS_RX_ANA0 value is hardcoded in the Allwinner BSP. */
0280     regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME2_REG,
0281              SUN6I_DPHY_RX_TIME2_HS_RX_ANA0(4));
0282 
0283     /*
0284      * Formula from the Allwinner BSP, with hardcoded coefficients
0285      * (probably internal divider/multiplier).
0286      */
0287     lprst_dly = 4 * (unsigned int)(dphy_clk_rate / (mipi_symbol_rate / 2));
0288 
0289     regmap_write(dphy->regs, SUN6I_DPHY_RX_TIME3_REG,
0290              SUN6I_DPHY_RX_TIME3_LPRST_DLY(lprst_dly));
0291 
0292     /* Analog parameters are hardcoded in the Allwinner BSP. */
0293     regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG,
0294              SUN6I_DPHY_ANA0_REG_PWS |
0295              SUN6I_DPHY_ANA0_REG_SLV(7) |
0296              SUN6I_DPHY_ANA0_REG_SFB(2));
0297 
0298     regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
0299              SUN6I_DPHY_ANA1_REG_SVTT(4));
0300 
0301     regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG,
0302              SUN6I_DPHY_ANA4_REG_DMPLVC |
0303              SUN6I_DPHY_ANA4_REG_DMPLVD(1));
0304 
0305     regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
0306              SUN6I_DPHY_ANA2_REG_ENIB);
0307 
0308     regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG,
0309              SUN6I_DPHY_ANA3_EN_LDOR |
0310              SUN6I_DPHY_ANA3_EN_LDOC |
0311              SUN6I_DPHY_ANA3_EN_LDOD);
0312 
0313     /*
0314      * Delay comes from the Allwinner BSP, likely for internal regulator
0315      * ramp-up.
0316      */
0317     udelay(3);
0318 
0319     value = SUN6I_DPHY_RX_CTL_EN_DBC | SUN6I_DPHY_RX_CTL_RX_CLK_FORCE;
0320 
0321     /*
0322      * Rx data lane force-enable bits are used as regular RX enable by the
0323      * Allwinner BSP.
0324      */
0325     if (dphy->config.lanes >= 1)
0326         value |= SUN6I_DPHY_RX_CTL_RX_D0_FORCE;
0327     if (dphy->config.lanes >= 2)
0328         value |= SUN6I_DPHY_RX_CTL_RX_D1_FORCE;
0329     if (dphy->config.lanes >= 3)
0330         value |= SUN6I_DPHY_RX_CTL_RX_D2_FORCE;
0331     if (dphy->config.lanes == 4)
0332         value |= SUN6I_DPHY_RX_CTL_RX_D3_FORCE;
0333 
0334     regmap_write(dphy->regs, SUN6I_DPHY_RX_CTL_REG, value);
0335 
0336     regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
0337              SUN6I_DPHY_GCTL_LANE_NUM(dphy->config.lanes) |
0338              SUN6I_DPHY_GCTL_EN);
0339 
0340     return 0;
0341 }
0342 
0343 static int sun6i_dphy_power_on(struct phy *phy)
0344 {
0345     struct sun6i_dphy *dphy = phy_get_drvdata(phy);
0346 
0347     switch (dphy->direction) {
0348     case SUN6I_DPHY_DIRECTION_TX:
0349         return sun6i_dphy_tx_power_on(dphy);
0350     case SUN6I_DPHY_DIRECTION_RX:
0351         return sun6i_dphy_rx_power_on(dphy);
0352     default:
0353         return -EINVAL;
0354     }
0355 }
0356 
0357 static int sun6i_dphy_power_off(struct phy *phy)
0358 {
0359     struct sun6i_dphy *dphy = phy_get_drvdata(phy);
0360 
0361     regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG, 0);
0362 
0363     regmap_write(dphy->regs, SUN6I_DPHY_ANA0_REG, 0);
0364     regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG, 0);
0365     regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG, 0);
0366     regmap_write(dphy->regs, SUN6I_DPHY_ANA3_REG, 0);
0367     regmap_write(dphy->regs, SUN6I_DPHY_ANA4_REG, 0);
0368 
0369     return 0;
0370 }
0371 
0372 static int sun6i_dphy_exit(struct phy *phy)
0373 {
0374     struct sun6i_dphy *dphy = phy_get_drvdata(phy);
0375 
0376     clk_rate_exclusive_put(dphy->mod_clk);
0377     clk_disable_unprepare(dphy->mod_clk);
0378     reset_control_assert(dphy->reset);
0379 
0380     return 0;
0381 }
0382 
0383 
0384 static const struct phy_ops sun6i_dphy_ops = {
0385     .configure  = sun6i_dphy_configure,
0386     .power_on   = sun6i_dphy_power_on,
0387     .power_off  = sun6i_dphy_power_off,
0388     .init       = sun6i_dphy_init,
0389     .exit       = sun6i_dphy_exit,
0390 };
0391 
0392 static const struct regmap_config sun6i_dphy_regmap_config = {
0393     .reg_bits   = 32,
0394     .val_bits   = 32,
0395     .reg_stride = 4,
0396     .max_register   = SUN6I_DPHY_DBG5_REG,
0397     .name       = "mipi-dphy",
0398 };
0399 
0400 static int sun6i_dphy_probe(struct platform_device *pdev)
0401 {
0402     struct phy_provider *phy_provider;
0403     struct sun6i_dphy *dphy;
0404     const char *direction;
0405     void __iomem *regs;
0406     int ret;
0407 
0408     dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL);
0409     if (!dphy)
0410         return -ENOMEM;
0411 
0412     regs = devm_platform_ioremap_resource(pdev, 0);
0413     if (IS_ERR(regs)) {
0414         dev_err(&pdev->dev, "Couldn't map the DPHY encoder registers\n");
0415         return PTR_ERR(regs);
0416     }
0417 
0418     dphy->regs = devm_regmap_init_mmio_clk(&pdev->dev, "bus",
0419                            regs, &sun6i_dphy_regmap_config);
0420     if (IS_ERR(dphy->regs)) {
0421         dev_err(&pdev->dev, "Couldn't create the DPHY encoder regmap\n");
0422         return PTR_ERR(dphy->regs);
0423     }
0424 
0425     dphy->reset = devm_reset_control_get_shared(&pdev->dev, NULL);
0426     if (IS_ERR(dphy->reset)) {
0427         dev_err(&pdev->dev, "Couldn't get our reset line\n");
0428         return PTR_ERR(dphy->reset);
0429     }
0430 
0431     dphy->mod_clk = devm_clk_get(&pdev->dev, "mod");
0432     if (IS_ERR(dphy->mod_clk)) {
0433         dev_err(&pdev->dev, "Couldn't get the DPHY mod clock\n");
0434         return PTR_ERR(dphy->mod_clk);
0435     }
0436 
0437     dphy->phy = devm_phy_create(&pdev->dev, NULL, &sun6i_dphy_ops);
0438     if (IS_ERR(dphy->phy)) {
0439         dev_err(&pdev->dev, "failed to create PHY\n");
0440         return PTR_ERR(dphy->phy);
0441     }
0442 
0443     dphy->direction = SUN6I_DPHY_DIRECTION_TX;
0444 
0445     ret = of_property_read_string(pdev->dev.of_node, "allwinner,direction",
0446                       &direction);
0447 
0448     if (!ret && !strncmp(direction, "rx", 2))
0449         dphy->direction = SUN6I_DPHY_DIRECTION_RX;
0450 
0451     phy_set_drvdata(dphy->phy, dphy);
0452     phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
0453 
0454     return PTR_ERR_OR_ZERO(phy_provider);
0455 }
0456 
0457 static const struct of_device_id sun6i_dphy_of_table[] = {
0458     { .compatible = "allwinner,sun6i-a31-mipi-dphy" },
0459     { }
0460 };
0461 MODULE_DEVICE_TABLE(of, sun6i_dphy_of_table);
0462 
0463 static struct platform_driver sun6i_dphy_platform_driver = {
0464     .probe      = sun6i_dphy_probe,
0465     .driver     = {
0466         .name       = "sun6i-mipi-dphy",
0467         .of_match_table = sun6i_dphy_of_table,
0468     },
0469 };
0470 module_platform_driver(sun6i_dphy_platform_driver);
0471 
0472 MODULE_AUTHOR("Maxime Ripard <maxime.ripard@bootlin>");
0473 MODULE_DESCRIPTION("Allwinner A31 MIPI D-PHY Driver");
0474 MODULE_LICENSE("GPL");