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/of_address.h>
12 #include <linux/regmap.h>
13 #include <linux/reset.h>
14
15 #include "sun6i_mipi_dsi.h"
16
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)
20
21 #define SUN6I_DPHY_TX_CTL_REG 0x04
22 #define SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT BIT(28)
23
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)
28
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)
34
35 #define SUN6I_DPHY_TX_TIME2_REG 0x18
36 #define SUN6I_DPHY_TX_TIME2_CLK_TRAIL(n) ((n) & 0xff)
37
38 #define SUN6I_DPHY_TX_TIME3_REG 0x1c
39
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)
43
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)
50
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)
55
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)
61
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)
70
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)
81
82 #define SUN6I_DPHY_DBG5_REG 0xf4
83
sun6i_dphy_init(struct sun6i_dphy * dphy,unsigned int lanes)84 int sun6i_dphy_init(struct sun6i_dphy *dphy, unsigned int lanes)
85 {
86 reset_control_deassert(dphy->reset);
87 clk_prepare_enable(dphy->mod_clk);
88 clk_set_rate_exclusive(dphy->mod_clk, 150000000);
89
90 regmap_write(dphy->regs, SUN6I_DPHY_TX_CTL_REG,
91 SUN6I_DPHY_TX_CTL_HS_TX_CLK_CONT);
92
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));
97
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));
103
104 regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME2_REG,
105 SUN6I_DPHY_TX_TIME2_CLK_TRAIL(30));
106
107 regmap_write(dphy->regs, SUN6I_DPHY_TX_TIME3_REG, 0);
108
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));
112
113 regmap_write(dphy->regs, SUN6I_DPHY_GCTL_REG,
114 SUN6I_DPHY_GCTL_LANE_NUM(lanes) |
115 SUN6I_DPHY_GCTL_EN);
116
117 return 0;
118 }
119
sun6i_dphy_power_on(struct sun6i_dphy * dphy,unsigned int lanes)120 int sun6i_dphy_power_on(struct sun6i_dphy *dphy, unsigned int lanes)
121 {
122 u8 lanes_mask = GENMASK(lanes - 1, 0);
123
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));
130
131 regmap_write(dphy->regs, SUN6I_DPHY_ANA1_REG,
132 SUN6I_DPHY_ANA1_REG_CSMPS(1) |
133 SUN6I_DPHY_ANA1_REG_SVTT(7));
134
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));
145
146 regmap_write(dphy->regs, SUN6I_DPHY_ANA2_REG,
147 SUN6I_DPHY_ANA2_REG_ENIB);
148 udelay(5);
149
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);
154 udelay(1);
155
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));
161 udelay(1);
162
163 regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA3_REG,
164 SUN6I_DPHY_ANA3_EN_DIV,
165 SUN6I_DPHY_ANA3_EN_DIV);
166 udelay(1);
167
168 regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA2_REG,
169 SUN6I_DPHY_ANA2_EN_CK_CPU,
170 SUN6I_DPHY_ANA2_EN_CK_CPU);
171 udelay(1);
172
173 regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
174 SUN6I_DPHY_ANA1_REG_VTTMODE,
175 SUN6I_DPHY_ANA1_REG_VTTMODE);
176
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));
180
181 return 0;
182 }
183
sun6i_dphy_power_off(struct sun6i_dphy * dphy)184 int sun6i_dphy_power_off(struct sun6i_dphy *dphy)
185 {
186 regmap_update_bits(dphy->regs, SUN6I_DPHY_ANA1_REG,
187 SUN6I_DPHY_ANA1_REG_VTTMODE, 0);
188
189 return 0;
190 }
191
sun6i_dphy_exit(struct sun6i_dphy * dphy)192 int sun6i_dphy_exit(struct sun6i_dphy *dphy)
193 {
194 clk_rate_exclusive_put(dphy->mod_clk);
195 clk_disable_unprepare(dphy->mod_clk);
196 reset_control_assert(dphy->reset);
197
198 return 0;
199 }
200
201 static struct regmap_config sun6i_dphy_regmap_config = {
202 .reg_bits = 32,
203 .val_bits = 32,
204 .reg_stride = 4,
205 .max_register = SUN6I_DPHY_DBG5_REG,
206 .name = "mipi-dphy",
207 };
208
209 static const struct of_device_id sun6i_dphy_of_table[] = {
210 { .compatible = "allwinner,sun6i-a31-mipi-dphy" },
211 { }
212 };
213
sun6i_dphy_probe(struct sun6i_dsi * dsi,struct device_node * node)214 int sun6i_dphy_probe(struct sun6i_dsi *dsi, struct device_node *node)
215 {
216 struct sun6i_dphy *dphy;
217 struct resource res;
218 void __iomem *regs;
219 int ret;
220
221 if (!of_match_node(sun6i_dphy_of_table, node)) {
222 dev_err(dsi->dev, "Incompatible D-PHY\n");
223 return -EINVAL;
224 }
225
226 dphy = devm_kzalloc(dsi->dev, sizeof(*dphy), GFP_KERNEL);
227 if (!dphy)
228 return -ENOMEM;
229
230 ret = of_address_to_resource(node, 0, &res);
231 if (ret) {
232 dev_err(dsi->dev, "phy: Couldn't get our resources\n");
233 return ret;
234 }
235
236 regs = devm_ioremap_resource(dsi->dev, &res);
237 if (IS_ERR(regs)) {
238 dev_err(dsi->dev, "Couldn't map the DPHY encoder registers\n");
239 return PTR_ERR(regs);
240 }
241
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);
247 }
248
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);
253 }
254
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);
259 goto err_free_reset;
260 }
261 regmap_mmio_attach_clk(dphy->regs, dphy->bus_clk);
262
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);
267 goto err_free_bus;
268 }
269
270 dsi->dphy = dphy;
271
272 return 0;
273
274 err_free_bus:
275 regmap_mmio_detach_clk(dphy->regs);
276 clk_put(dphy->bus_clk);
277 err_free_reset:
278 reset_control_put(dphy->reset);
279 return ret;
280 }
281
sun6i_dphy_remove(struct sun6i_dsi * dsi)282 int sun6i_dphy_remove(struct sun6i_dsi *dsi)
283 {
284 struct sun6i_dphy *dphy = dsi->dphy;
285
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);
290
291 return 0;
292 }
293