1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (c) 2016 Allwinnertech Co., Ltd.
4 * Copyright (C) 2017-2018 Bootlin
6 * Maxime Ripard <maxime.ripard@free-electrons.com>
9 #include <linux/bitops.h>
10 #include <linux/clk.h>
11 #include <linux/of_address.h>
12 #include <linux/regmap.h>
13 #include <linux/reset.h>
15 #include "sun6i_mipi_dsi.h"
17 #define SUN6I_DPHY_GCTL_REG 0x00
18 #define SUN6I_DPHY_GCTL_LANE_NUM(n) ((((n) - 1) & 3) << 4)
19 #define SUN6I_DPHY_GCTL_EN BIT(0)
21 #define SUN6I_DPHY_TX_CTL_REG 0x04
22 #define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT BIT(28)
24 #define SUN6I_DPHY_TX_TIME0_REG 0x10
25 #define SUN6I_DPHY_TX_TIME0_HS_TRAIL(n) (((n) & 0xff) << 24)
26 #define SUN6I_DPHY_TX_TIME0_HS_PREPARE(n) (((n) & 0xff) << 16)
27 #define SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(n) ((n) & 0xff)
29 #define SUN6I_DPHY_TX_TIME1_REG 0x14
30 #define SUN6I_DPHY_TX_TIME1_CLK_POST(n) (((n) & 0xff) << 24)
31 #define SUN6I_DPHY_TX_TIME1_CLK_PRE(n) (((n) & 0xff) << 16)
32 #define SUN6I_DPHY_TX_TIME1_CLK_ZERO(n) (((n) & 0xff) << 8)
33 #define SUN6I_DPHY_TX_TIME1_CLK_PREPARE(n) ((n) & 0xff)
35 #define SUN6I_DPHY_TX_TIME2_REG 0x18
36 #define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n) ((n) & 0xff)
38 #define SUN6I_DPHY_TX_TIME3_REG 0x1c
40 #define SUN6I_DPHY_TX_TIME4_REG 0x20
41 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(n) (((n) & 0xff) << 8)
42 #define SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(n) ((n) & 0xff)
44 #define SUN6I_DPHY_ANA0_REG 0x4c
45 #define SUN6I_DPHY_ANA0_REG_PWS BIT(31)
46 #define SUN6I_DPHY_ANA0_REG_DMPC BIT(28)
47 #define SUN6I_DPHY_ANA0_REG_DMPD(n) (((n) & 0xf) << 24)
48 #define SUN6I_DPHY_ANA0_REG_SLV(n) (((n) & 7) << 12)
49 #define SUN6I_DPHY_ANA0_REG_DEN(n) (((n) & 0xf) << 8)
51 #define SUN6I_DPHY_ANA1_REG 0x50
52 #define SUN6I_DPHY_ANA1_REG_VTTMODE BIT(31)
53 #define SUN6I_DPHY_ANA1_REG_CSMPS(n) (((n) & 3) << 28)
54 #define SUN6I_DPHY_ANA1_REG_SVTT(n) (((n) & 0xf) << 24)
56 #define SUN6I_DPHY_ANA2_REG 0x54
57 #define SUN6I_DPHY_ANA2_EN_P2S_CPU(n) (((n) & 0xf) << 24)
58 #define SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK GENMASK(27, 24)
59 #define SUN6I_DPHY_ANA2_EN_CK_CPU BIT(4)
60 #define SUN6I_DPHY_ANA2_REG_ENIB BIT(1)
62 #define SUN6I_DPHY_ANA3_REG 0x58
63 #define SUN6I_DPHY_ANA3_EN_VTTD(n) (((n) & 0xf) << 28)
64 #define SUN6I_DPHY_ANA3_EN_VTTD_MASK GENMASK(31, 28)
65 #define SUN6I_DPHY_ANA3_EN_VTTC BIT(27)
66 #define SUN6I_DPHY_ANA3_EN_DIV BIT(26)
67 #define SUN6I_DPHY_ANA3_EN_LDOC BIT(25)
68 #define SUN6I_DPHY_ANA3_EN_LDOD BIT(24)
69 #define SUN6I_DPHY_ANA3_EN_LDOR BIT(18)
71 #define SUN6I_DPHY_ANA4_REG 0x5c
72 #define SUN6I_DPHY_ANA4_REG_DMPLVC BIT(24)
73 #define SUN6I_DPHY_ANA4_REG_DMPLVD(n) (((n) & 0xf) << 20)
74 #define SUN6I_DPHY_ANA4_REG_CKDV(n) (((n) & 0x1f) << 12)
75 #define SUN6I_DPHY_ANA4_REG_TMSC(n) (((n) & 3) << 10)
76 #define SUN6I_DPHY_ANA4_REG_TMSD(n) (((n) & 3) << 8)
77 #define SUN6I_DPHY_ANA4_REG_TXDNSC(n) (((n) & 3) << 6)
78 #define SUN6I_DPHY_ANA4_REG_TXDNSD(n) (((n) & 3) << 4)
79 #define SUN6I_DPHY_ANA4_REG_TXPUSC(n) (((n) & 3) << 2)
80 #define SUN6I_DPHY_ANA4_REG_TXPUSD(n) ((n) & 3)
82 #define SUN6I_DPHY_DBG5_REG 0xf4
84 int sun6i_dphy_init(struct sun6i_dphy
*dphy
, unsigned int lanes
)
86 reset_control_deassert(dphy
->reset
);
87 clk_prepare_enable(dphy
->mod_clk
);
88 clk_set_rate_exclusive(dphy
->mod_clk
, 150000000);
90 regmap_write(dphy
->regs
, SUN6I_DPHY_TX_CTL_REG
,
91 SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT
);
93 regmap_write(dphy
->regs
, SUN6I_DPHY_TX_TIME0_REG
,
94 SUN6I_DPHY_TX_TIME0_LP_CLK_DIV(14) |
95 SUN6I_DPHY_TX_TIME0_HS_PREPARE(6) |
96 SUN6I_DPHY_TX_TIME0_HS_TRAIL(10));
98 regmap_write(dphy
->regs
, SUN6I_DPHY_TX_TIME1_REG
,
99 SUN6I_DPHY_TX_TIME1_CLK_PREPARE(7) |
100 SUN6I_DPHY_TX_TIME1_CLK_ZERO(50) |
101 SUN6I_DPHY_TX_TIME1_CLK_PRE(3) |
102 SUN6I_DPHY_TX_TIME1_CLK_POST(10));
104 regmap_write(dphy
->regs
, SUN6I_DPHY_TX_TIME2_REG
,
105 SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30));
107 regmap_write(dphy
->regs
, SUN6I_DPHY_TX_TIME3_REG
, 0);
109 regmap_write(dphy
->regs
, SUN6I_DPHY_TX_TIME4_REG
,
110 SUN6I_DPHY_TX_TIME4_HS_TX_ANA0(3) |
111 SUN6I_DPHY_TX_TIME4_HS_TX_ANA1(3));
113 regmap_write(dphy
->regs
, SUN6I_DPHY_GCTL_REG
,
114 SUN6I_DPHY_GCTL_LANE_NUM(lanes
) |
120 int sun6i_dphy_power_on(struct sun6i_dphy
*dphy
, unsigned int lanes
)
122 u8 lanes_mask
= GENMASK(lanes
- 1, 0);
124 regmap_write(dphy
->regs
, SUN6I_DPHY_ANA0_REG
,
125 SUN6I_DPHY_ANA0_REG_PWS
|
126 SUN6I_DPHY_ANA0_REG_DMPC
|
127 SUN6I_DPHY_ANA0_REG_SLV(7) |
128 SUN6I_DPHY_ANA0_REG_DMPD(lanes_mask
) |
129 SUN6I_DPHY_ANA0_REG_DEN(lanes_mask
));
131 regmap_write(dphy
->regs
, SUN6I_DPHY_ANA1_REG
,
132 SUN6I_DPHY_ANA1_REG_CSMPS(1) |
133 SUN6I_DPHY_ANA1_REG_SVTT(7));
135 regmap_write(dphy
->regs
, SUN6I_DPHY_ANA4_REG
,
136 SUN6I_DPHY_ANA4_REG_CKDV(1) |
137 SUN6I_DPHY_ANA4_REG_TMSC(1) |
138 SUN6I_DPHY_ANA4_REG_TMSD(1) |
139 SUN6I_DPHY_ANA4_REG_TXDNSC(1) |
140 SUN6I_DPHY_ANA4_REG_TXDNSD(1) |
141 SUN6I_DPHY_ANA4_REG_TXPUSC(1) |
142 SUN6I_DPHY_ANA4_REG_TXPUSD(1) |
143 SUN6I_DPHY_ANA4_REG_DMPLVC
|
144 SUN6I_DPHY_ANA4_REG_DMPLVD(lanes_mask
));
146 regmap_write(dphy
->regs
, SUN6I_DPHY_ANA2_REG
,
147 SUN6I_DPHY_ANA2_REG_ENIB
);
150 regmap_write(dphy
->regs
, SUN6I_DPHY_ANA3_REG
,
151 SUN6I_DPHY_ANA3_EN_LDOR
|
152 SUN6I_DPHY_ANA3_EN_LDOC
|
153 SUN6I_DPHY_ANA3_EN_LDOD
);
156 regmap_update_bits(dphy
->regs
, SUN6I_DPHY_ANA3_REG
,
157 SUN6I_DPHY_ANA3_EN_VTTC
|
158 SUN6I_DPHY_ANA3_EN_VTTD_MASK
,
159 SUN6I_DPHY_ANA3_EN_VTTC
|
160 SUN6I_DPHY_ANA3_EN_VTTD(lanes_mask
));
163 regmap_update_bits(dphy
->regs
, SUN6I_DPHY_ANA3_REG
,
164 SUN6I_DPHY_ANA3_EN_DIV
,
165 SUN6I_DPHY_ANA3_EN_DIV
);
168 regmap_update_bits(dphy
->regs
, SUN6I_DPHY_ANA2_REG
,
169 SUN6I_DPHY_ANA2_EN_CK_CPU
,
170 SUN6I_DPHY_ANA2_EN_CK_CPU
);
173 regmap_update_bits(dphy
->regs
, SUN6I_DPHY_ANA1_REG
,
174 SUN6I_DPHY_ANA1_REG_VTTMODE
,
175 SUN6I_DPHY_ANA1_REG_VTTMODE
);
177 regmap_update_bits(dphy
->regs
, SUN6I_DPHY_ANA2_REG
,
178 SUN6I_DPHY_ANA2_EN_P2S_CPU_MASK
,
179 SUN6I_DPHY_ANA2_EN_P2S_CPU(lanes_mask
));
184 int sun6i_dphy_power_off(struct sun6i_dphy
*dphy
)
186 regmap_update_bits(dphy
->regs
, SUN6I_DPHY_ANA1_REG
,
187 SUN6I_DPHY_ANA1_REG_VTTMODE
, 0);
192 int sun6i_dphy_exit(struct sun6i_dphy
*dphy
)
194 clk_rate_exclusive_put(dphy
->mod_clk
);
195 clk_disable_unprepare(dphy
->mod_clk
);
196 reset_control_assert(dphy
->reset
);
201 static struct regmap_config sun6i_dphy_regmap_config
= {
205 .max_register
= SUN6I_DPHY_DBG5_REG
,
209 static const struct of_device_id sun6i_dphy_of_table
[] = {
210 { .compatible
= "allwinner,sun6i-a31-mipi-dphy" },
214 int sun6i_dphy_probe(struct sun6i_dsi
*dsi
, struct device_node
*node
)
216 struct sun6i_dphy
*dphy
;
221 if (!of_match_node(sun6i_dphy_of_table
, node
)) {
222 dev_err(dsi
->dev
, "Incompatible D-PHY\n");
226 dphy
= devm_kzalloc(dsi
->dev
, sizeof(*dphy
), GFP_KERNEL
);
230 ret
= of_address_to_resource(node
, 0, &res
);
232 dev_err(dsi
->dev
, "phy: Couldn't get our resources\n");
236 regs
= devm_ioremap_resource(dsi
->dev
, &res
);
238 dev_err(dsi
->dev
, "Couldn't map the DPHY encoder registers\n");
239 return PTR_ERR(regs
);
242 dphy
->regs
= devm_regmap_init_mmio(dsi
->dev
, regs
,
243 &sun6i_dphy_regmap_config
);
244 if (IS_ERR(dphy
->regs
)) {
245 dev_err(dsi
->dev
, "Couldn't create the DPHY encoder regmap\n");
246 return PTR_ERR(dphy
->regs
);
249 dphy
->reset
= of_reset_control_get_shared(node
, NULL
);
250 if (IS_ERR(dphy
->reset
)) {
251 dev_err(dsi
->dev
, "Couldn't get our reset line\n");
252 return PTR_ERR(dphy
->reset
);
255 dphy
->bus_clk
= of_clk_get_by_name(node
, "bus");
256 if (IS_ERR(dphy
->bus_clk
)) {
257 dev_err(dsi
->dev
, "Couldn't get the DPHY bus clock\n");
258 ret
= PTR_ERR(dphy
->bus_clk
);
261 regmap_mmio_attach_clk(dphy
->regs
, dphy
->bus_clk
);
263 dphy
->mod_clk
= of_clk_get_by_name(node
, "mod");
264 if (IS_ERR(dphy
->mod_clk
)) {
265 dev_err(dsi
->dev
, "Couldn't get the DPHY mod clock\n");
266 ret
= PTR_ERR(dphy
->mod_clk
);
275 regmap_mmio_detach_clk(dphy
->regs
);
276 clk_put(dphy
->bus_clk
);
278 reset_control_put(dphy
->reset
);
282 int sun6i_dphy_remove(struct sun6i_dsi
*dsi
)
284 struct sun6i_dphy
*dphy
= dsi
->dphy
;
286 regmap_mmio_detach_clk(dphy
->regs
);
287 clk_put(dphy
->mod_clk
);
288 clk_put(dphy
->bus_clk
);
289 reset_control_put(dphy
->reset
);