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@bootlin.com> |
7 | */ |
8 | |
9 | #include <linux/clk.h> |
10 | #include <linux/component.h> |
11 | #include <linux/crc-ccitt.h> |
12 | #include <linux/module.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/phy/phy-mipi-dphy.h> |
15 | #include <linux/phy/phy.h> |
16 | #include <linux/platform_device.h> |
17 | #include <linux/regmap.h> |
18 | #include <linux/regulator/consumer.h> |
19 | #include <linux/reset.h> |
20 | #include <linux/slab.h> |
21 | |
22 | #include <drm/drm_atomic_helper.h> |
23 | #include <drm/drm_mipi_dsi.h> |
24 | #include <drm/drm_panel.h> |
25 | #include <drm/drm_print.h> |
26 | #include <drm/drm_probe_helper.h> |
27 | #include <drm/drm_simple_kms_helper.h> |
28 | |
29 | #include "sun4i_crtc.h" |
30 | #include "sun4i_tcon.h" |
31 | #include "sun6i_mipi_dsi.h" |
32 | |
33 | #include <video/mipi_display.h> |
34 | |
35 | #define SUN6I_DSI_CTL_REG 0x000 |
36 | #define SUN6I_DSI_CTL_EN BIT(0) |
37 | |
38 | #define SUN6I_DSI_BASIC_CTL_REG 0x00c |
39 | #define SUN6I_DSI_BASIC_CTL_TRAIL_INV(n) (((n) & 0xf) << 4) |
40 | #define SUN6I_DSI_BASIC_CTL_TRAIL_FILL BIT(3) |
41 | #define SUN6I_DSI_BASIC_CTL_HBP_DIS BIT(2) |
42 | #define SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS BIT(1) |
43 | #define SUN6I_DSI_BASIC_CTL_VIDEO_BURST BIT(0) |
44 | |
45 | #define SUN6I_DSI_BASIC_CTL0_REG 0x010 |
46 | #define SUN6I_DSI_BASIC_CTL0_HS_EOTP_EN BIT(18) |
47 | #define SUN6I_DSI_BASIC_CTL0_CRC_EN BIT(17) |
48 | #define SUN6I_DSI_BASIC_CTL0_ECC_EN BIT(16) |
49 | #define SUN6I_DSI_BASIC_CTL0_INST_ST BIT(0) |
50 | |
51 | #define SUN6I_DSI_BASIC_CTL1_REG 0x014 |
52 | #define SUN6I_DSI_BASIC_CTL1_VIDEO_ST_DELAY(n) (((n) & 0x1fff) << 4) |
53 | #define SUN6I_DSI_BASIC_CTL1_VIDEO_FILL BIT(2) |
54 | #define SUN6I_DSI_BASIC_CTL1_VIDEO_PRECISION BIT(1) |
55 | #define SUN6I_DSI_BASIC_CTL1_VIDEO_MODE BIT(0) |
56 | |
57 | #define SUN6I_DSI_BASIC_SIZE0_REG 0x018 |
58 | #define SUN6I_DSI_BASIC_SIZE0_VBP(n) (((n) & 0xfff) << 16) |
59 | #define SUN6I_DSI_BASIC_SIZE0_VSA(n) ((n) & 0xfff) |
60 | |
61 | #define SUN6I_DSI_BASIC_SIZE1_REG 0x01c |
62 | #define SUN6I_DSI_BASIC_SIZE1_VT(n) (((n) & 0xfff) << 16) |
63 | #define SUN6I_DSI_BASIC_SIZE1_VACT(n) ((n) & 0xfff) |
64 | |
65 | #define SUN6I_DSI_INST_FUNC_REG(n) (0x020 + (n) * 0x04) |
66 | #define SUN6I_DSI_INST_FUNC_INST_MODE(n) (((n) & 0xf) << 28) |
67 | #define SUN6I_DSI_INST_FUNC_ESCAPE_ENTRY(n) (((n) & 0xf) << 24) |
68 | #define SUN6I_DSI_INST_FUNC_TRANS_PACKET(n) (((n) & 0xf) << 20) |
69 | #define SUN6I_DSI_INST_FUNC_LANE_CEN BIT(4) |
70 | #define SUN6I_DSI_INST_FUNC_LANE_DEN(n) ((n) & 0xf) |
71 | |
72 | #define SUN6I_DSI_INST_LOOP_SEL_REG 0x040 |
73 | |
74 | #define SUN6I_DSI_INST_LOOP_NUM_REG(n) (0x044 + (n) * 0x10) |
75 | #define SUN6I_DSI_INST_LOOP_NUM_N1(n) (((n) & 0xfff) << 16) |
76 | #define SUN6I_DSI_INST_LOOP_NUM_N0(n) ((n) & 0xfff) |
77 | |
78 | #define SUN6I_DSI_INST_JUMP_SEL_REG 0x048 |
79 | |
80 | #define SUN6I_DSI_INST_JUMP_CFG_REG(n) (0x04c + (n) * 0x04) |
81 | #define SUN6I_DSI_INST_JUMP_CFG_TO(n) (((n) & 0xf) << 20) |
82 | #define SUN6I_DSI_INST_JUMP_CFG_POINT(n) (((n) & 0xf) << 16) |
83 | #define SUN6I_DSI_INST_JUMP_CFG_NUM(n) ((n) & 0xffff) |
84 | |
85 | #define SUN6I_DSI_TRANS_START_REG 0x060 |
86 | |
87 | #define SUN6I_DSI_TRANS_ZERO_REG 0x078 |
88 | |
89 | #define SUN6I_DSI_TCON_DRQ_REG 0x07c |
90 | #define SUN6I_DSI_TCON_DRQ_ENABLE_MODE BIT(28) |
91 | #define SUN6I_DSI_TCON_DRQ_SET(n) ((n) & 0x3ff) |
92 | |
93 | #define SUN6I_DSI_PIXEL_CTL0_REG 0x080 |
94 | #define SUN6I_DSI_PIXEL_CTL0_PD_PLUG_DISABLE BIT(16) |
95 | #define SUN6I_DSI_PIXEL_CTL0_FORMAT(n) ((n) & 0xf) |
96 | |
97 | #define SUN6I_DSI_PIXEL_CTL1_REG 0x084 |
98 | |
99 | #define SUN6I_DSI_PIXEL_PH_REG 0x090 |
100 | #define SUN6I_DSI_PIXEL_PH_ECC(n) (((n) & 0xff) << 24) |
101 | #define SUN6I_DSI_PIXEL_PH_WC(n) (((n) & 0xffff) << 8) |
102 | #define SUN6I_DSI_PIXEL_PH_VC(n) (((n) & 3) << 6) |
103 | #define SUN6I_DSI_PIXEL_PH_DT(n) ((n) & 0x3f) |
104 | |
105 | #define SUN6I_DSI_PIXEL_PF0_REG 0x098 |
106 | #define SUN6I_DSI_PIXEL_PF0_CRC_FORCE(n) ((n) & 0xffff) |
107 | |
108 | #define SUN6I_DSI_PIXEL_PF1_REG 0x09c |
109 | #define SUN6I_DSI_PIXEL_PF1_CRC_INIT_LINEN(n) (((n) & 0xffff) << 16) |
110 | #define SUN6I_DSI_PIXEL_PF1_CRC_INIT_LINE0(n) ((n) & 0xffff) |
111 | |
112 | #define SUN6I_DSI_SYNC_HSS_REG 0x0b0 |
113 | |
114 | #define SUN6I_DSI_SYNC_HSE_REG 0x0b4 |
115 | |
116 | #define SUN6I_DSI_SYNC_VSS_REG 0x0b8 |
117 | |
118 | #define SUN6I_DSI_SYNC_VSE_REG 0x0bc |
119 | |
120 | #define SUN6I_DSI_BLK_HSA0_REG 0x0c0 |
121 | |
122 | #define SUN6I_DSI_BLK_HSA1_REG 0x0c4 |
123 | #define SUN6I_DSI_BLK_PF(n) (((n) & 0xffff) << 16) |
124 | #define SUN6I_DSI_BLK_PD(n) ((n) & 0xff) |
125 | |
126 | #define SUN6I_DSI_BLK_HBP0_REG 0x0c8 |
127 | |
128 | #define SUN6I_DSI_BLK_HBP1_REG 0x0cc |
129 | |
130 | #define SUN6I_DSI_BLK_HFP0_REG 0x0d0 |
131 | |
132 | #define SUN6I_DSI_BLK_HFP1_REG 0x0d4 |
133 | |
134 | #define SUN6I_DSI_BLK_HBLK0_REG 0x0e0 |
135 | |
136 | #define SUN6I_DSI_BLK_HBLK1_REG 0x0e4 |
137 | |
138 | #define SUN6I_DSI_BLK_VBLK0_REG 0x0e8 |
139 | |
140 | #define SUN6I_DSI_BLK_VBLK1_REG 0x0ec |
141 | |
142 | #define SUN6I_DSI_BURST_LINE_REG 0x0f0 |
143 | #define SUN6I_DSI_BURST_LINE_SYNC_POINT(n) (((n) & 0xffff) << 16) |
144 | #define SUN6I_DSI_BURST_LINE_NUM(n) ((n) & 0xffff) |
145 | |
146 | #define SUN6I_DSI_BURST_DRQ_REG 0x0f4 |
147 | #define SUN6I_DSI_BURST_DRQ_EDGE1(n) (((n) & 0xffff) << 16) |
148 | #define SUN6I_DSI_BURST_DRQ_EDGE0(n) ((n) & 0xffff) |
149 | |
150 | #define SUN6I_DSI_CMD_CTL_REG 0x200 |
151 | #define SUN6I_DSI_CMD_CTL_RX_OVERFLOW BIT(26) |
152 | #define SUN6I_DSI_CMD_CTL_RX_FLAG BIT(25) |
153 | #define SUN6I_DSI_CMD_CTL_TX_FLAG BIT(9) |
154 | |
155 | #define SUN6I_DSI_CMD_RX_REG(n) (0x240 + (n) * 0x04) |
156 | |
157 | #define SUN6I_DSI_DEBUG_DATA_REG 0x2f8 |
158 | |
159 | #define SUN6I_DSI_CMD_TX_REG(n) (0x300 + (n) * 0x04) |
160 | |
161 | #define SUN6I_DSI_SYNC_POINT 40 |
162 | |
163 | enum sun6i_dsi_start_inst { |
164 | DSI_START_LPRX, |
165 | DSI_START_LPTX, |
166 | DSI_START_HSC, |
167 | DSI_START_HSD, |
168 | }; |
169 | |
170 | enum sun6i_dsi_inst_id { |
171 | DSI_INST_ID_LP11 = 0, |
172 | DSI_INST_ID_TBA, |
173 | DSI_INST_ID_HSC, |
174 | DSI_INST_ID_HSD, |
175 | DSI_INST_ID_LPDT, |
176 | DSI_INST_ID_HSCEXIT, |
177 | DSI_INST_ID_NOP, |
178 | DSI_INST_ID_DLY, |
179 | DSI_INST_ID_END = 15, |
180 | }; |
181 | |
182 | enum sun6i_dsi_inst_mode { |
183 | DSI_INST_MODE_STOP = 0, |
184 | DSI_INST_MODE_TBA, |
185 | DSI_INST_MODE_HS, |
186 | DSI_INST_MODE_ESCAPE, |
187 | DSI_INST_MODE_HSCEXIT, |
188 | DSI_INST_MODE_NOP, |
189 | }; |
190 | |
191 | enum sun6i_dsi_inst_escape { |
192 | DSI_INST_ESCA_LPDT = 0, |
193 | DSI_INST_ESCA_ULPS, |
194 | DSI_INST_ESCA_UN1, |
195 | DSI_INST_ESCA_UN2, |
196 | DSI_INST_ESCA_RESET, |
197 | DSI_INST_ESCA_UN3, |
198 | DSI_INST_ESCA_UN4, |
199 | DSI_INST_ESCA_UN5, |
200 | }; |
201 | |
202 | enum sun6i_dsi_inst_packet { |
203 | DSI_INST_PACK_PIXEL = 0, |
204 | DSI_INST_PACK_COMMAND, |
205 | }; |
206 | |
207 | static const u32 sun6i_dsi_ecc_array[] = { |
208 | [0] = (BIT(0) | BIT(1) | BIT(2) | BIT(4) | BIT(5) | BIT(7) | BIT(10) | |
209 | BIT(11) | BIT(13) | BIT(16) | BIT(20) | BIT(21) | BIT(22) | |
210 | BIT(23)), |
211 | [1] = (BIT(0) | BIT(1) | BIT(3) | BIT(4) | BIT(6) | BIT(8) | BIT(10) | |
212 | BIT(12) | BIT(14) | BIT(17) | BIT(20) | BIT(21) | BIT(22) | |
213 | BIT(23)), |
214 | [2] = (BIT(0) | BIT(2) | BIT(3) | BIT(5) | BIT(6) | BIT(9) | BIT(11) | |
215 | BIT(12) | BIT(15) | BIT(18) | BIT(20) | BIT(21) | BIT(22)), |
216 | [3] = (BIT(1) | BIT(2) | BIT(3) | BIT(7) | BIT(8) | BIT(9) | BIT(13) | |
217 | BIT(14) | BIT(15) | BIT(19) | BIT(20) | BIT(21) | BIT(23)), |
218 | [4] = (BIT(4) | BIT(5) | BIT(6) | BIT(7) | BIT(8) | BIT(9) | BIT(16) | |
219 | BIT(17) | BIT(18) | BIT(19) | BIT(20) | BIT(22) | BIT(23)), |
220 | [5] = (BIT(10) | BIT(11) | BIT(12) | BIT(13) | BIT(14) | BIT(15) | |
221 | BIT(16) | BIT(17) | BIT(18) | BIT(19) | BIT(21) | BIT(22) | |
222 | BIT(23)), |
223 | }; |
224 | |
225 | static u32 sun6i_dsi_ecc_compute(unsigned int data) |
226 | { |
227 | int i; |
228 | u8 ecc = 0; |
229 | |
230 | for (i = 0; i < ARRAY_SIZE(sun6i_dsi_ecc_array); i++) { |
231 | u32 field = sun6i_dsi_ecc_array[i]; |
232 | bool init = false; |
233 | u8 val = 0; |
234 | int j; |
235 | |
236 | for (j = 0; j < 24; j++) { |
237 | if (!(BIT(j) & field)) |
238 | continue; |
239 | |
240 | if (!init) { |
241 | val = (BIT(j) & data) ? 1 : 0; |
242 | init = true; |
243 | } else { |
244 | val ^= (BIT(j) & data) ? 1 : 0; |
245 | } |
246 | } |
247 | |
248 | ecc |= val << i; |
249 | } |
250 | |
251 | return ecc; |
252 | } |
253 | |
254 | static u16 sun6i_dsi_crc_compute(u8 const *buffer, size_t len) |
255 | { |
256 | return crc_ccitt(crc: 0xffff, buffer, len); |
257 | } |
258 | |
259 | static u16 sun6i_dsi_crc_repeat(u8 pd, u8 *buffer, size_t len) |
260 | { |
261 | memset(buffer, pd, len); |
262 | |
263 | return sun6i_dsi_crc_compute(buffer, len); |
264 | } |
265 | |
266 | static u32 sun6i_dsi_build_sync_pkt(u8 dt, u8 vc, u8 d0, u8 d1) |
267 | { |
268 | u32 val = dt & 0x3f; |
269 | |
270 | val |= (vc & 3) << 6; |
271 | val |= (d0 & 0xff) << 8; |
272 | val |= (d1 & 0xff) << 16; |
273 | val |= sun6i_dsi_ecc_compute(data: val) << 24; |
274 | |
275 | return val; |
276 | } |
277 | |
278 | static u32 sun6i_dsi_build_blk0_pkt(u8 vc, u16 wc) |
279 | { |
280 | return sun6i_dsi_build_sync_pkt(dt: MIPI_DSI_BLANKING_PACKET, vc, |
281 | d0: wc & 0xff, d1: wc >> 8); |
282 | } |
283 | |
284 | static u32 sun6i_dsi_build_blk1_pkt(u16 pd, u8 *buffer, size_t len) |
285 | { |
286 | u32 val = SUN6I_DSI_BLK_PD(pd); |
287 | |
288 | return val | SUN6I_DSI_BLK_PF(sun6i_dsi_crc_repeat(pd, buffer, len)); |
289 | } |
290 | |
291 | static void sun6i_dsi_inst_abort(struct sun6i_dsi *dsi) |
292 | { |
293 | regmap_update_bits(map: dsi->regs, SUN6I_DSI_BASIC_CTL0_REG, |
294 | SUN6I_DSI_BASIC_CTL0_INST_ST, val: 0); |
295 | } |
296 | |
297 | static void sun6i_dsi_inst_commit(struct sun6i_dsi *dsi) |
298 | { |
299 | regmap_update_bits(map: dsi->regs, SUN6I_DSI_BASIC_CTL0_REG, |
300 | SUN6I_DSI_BASIC_CTL0_INST_ST, |
301 | SUN6I_DSI_BASIC_CTL0_INST_ST); |
302 | } |
303 | |
304 | static int sun6i_dsi_inst_wait_for_completion(struct sun6i_dsi *dsi) |
305 | { |
306 | u32 val; |
307 | |
308 | return regmap_read_poll_timeout(dsi->regs, SUN6I_DSI_BASIC_CTL0_REG, |
309 | val, |
310 | !(val & SUN6I_DSI_BASIC_CTL0_INST_ST), |
311 | 100, 5000); |
312 | } |
313 | |
314 | static void sun6i_dsi_inst_setup(struct sun6i_dsi *dsi, |
315 | enum sun6i_dsi_inst_id id, |
316 | enum sun6i_dsi_inst_mode mode, |
317 | bool clock, u8 data, |
318 | enum sun6i_dsi_inst_packet packet, |
319 | enum sun6i_dsi_inst_escape escape) |
320 | { |
321 | regmap_write(map: dsi->regs, SUN6I_DSI_INST_FUNC_REG(id), |
322 | SUN6I_DSI_INST_FUNC_INST_MODE(mode) | |
323 | SUN6I_DSI_INST_FUNC_ESCAPE_ENTRY(escape) | |
324 | SUN6I_DSI_INST_FUNC_TRANS_PACKET(packet) | |
325 | (clock ? SUN6I_DSI_INST_FUNC_LANE_CEN : 0) | |
326 | SUN6I_DSI_INST_FUNC_LANE_DEN(data)); |
327 | } |
328 | |
329 | static void sun6i_dsi_inst_init(struct sun6i_dsi *dsi, |
330 | struct mipi_dsi_device *device) |
331 | { |
332 | u8 lanes_mask = GENMASK(device->lanes - 1, 0); |
333 | |
334 | sun6i_dsi_inst_setup(dsi, id: DSI_INST_ID_LP11, mode: DSI_INST_MODE_STOP, |
335 | clock: true, data: lanes_mask, packet: 0, escape: 0); |
336 | |
337 | sun6i_dsi_inst_setup(dsi, id: DSI_INST_ID_TBA, mode: DSI_INST_MODE_TBA, |
338 | clock: false, data: 1, packet: 0, escape: 0); |
339 | |
340 | sun6i_dsi_inst_setup(dsi, id: DSI_INST_ID_HSC, mode: DSI_INST_MODE_HS, |
341 | clock: true, data: 0, packet: DSI_INST_PACK_PIXEL, escape: 0); |
342 | |
343 | sun6i_dsi_inst_setup(dsi, id: DSI_INST_ID_HSD, mode: DSI_INST_MODE_HS, |
344 | clock: false, data: lanes_mask, packet: DSI_INST_PACK_PIXEL, escape: 0); |
345 | |
346 | sun6i_dsi_inst_setup(dsi, id: DSI_INST_ID_LPDT, mode: DSI_INST_MODE_ESCAPE, |
347 | clock: false, data: 1, packet: DSI_INST_PACK_COMMAND, |
348 | escape: DSI_INST_ESCA_LPDT); |
349 | |
350 | sun6i_dsi_inst_setup(dsi, id: DSI_INST_ID_HSCEXIT, mode: DSI_INST_MODE_HSCEXIT, |
351 | clock: true, data: 0, packet: 0, escape: 0); |
352 | |
353 | sun6i_dsi_inst_setup(dsi, id: DSI_INST_ID_NOP, mode: DSI_INST_MODE_STOP, |
354 | clock: false, data: lanes_mask, packet: 0, escape: 0); |
355 | |
356 | sun6i_dsi_inst_setup(dsi, id: DSI_INST_ID_DLY, mode: DSI_INST_MODE_NOP, |
357 | clock: true, data: lanes_mask, packet: 0, escape: 0); |
358 | |
359 | regmap_write(map: dsi->regs, SUN6I_DSI_INST_JUMP_CFG_REG(0), |
360 | SUN6I_DSI_INST_JUMP_CFG_POINT(DSI_INST_ID_NOP) | |
361 | SUN6I_DSI_INST_JUMP_CFG_TO(DSI_INST_ID_HSCEXIT) | |
362 | SUN6I_DSI_INST_JUMP_CFG_NUM(1)); |
363 | }; |
364 | |
365 | static u16 sun6i_dsi_get_video_start_delay(struct sun6i_dsi *dsi, |
366 | struct drm_display_mode *mode) |
367 | { |
368 | u16 delay = mode->vtotal - (mode->vsync_start - mode->vdisplay) + 1; |
369 | |
370 | if (delay > mode->vtotal) |
371 | delay = delay % mode->vtotal; |
372 | |
373 | return max_t(u16, delay, 1); |
374 | } |
375 | |
376 | static u16 sun6i_dsi_get_line_num(struct sun6i_dsi *dsi, |
377 | struct drm_display_mode *mode) |
378 | { |
379 | struct mipi_dsi_device *device = dsi->device; |
380 | unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(fmt: device->format) / 8; |
381 | |
382 | return mode->htotal * Bpp / device->lanes; |
383 | } |
384 | |
385 | static u16 sun6i_dsi_get_drq_edge0(struct sun6i_dsi *dsi, |
386 | struct drm_display_mode *mode, |
387 | u16 line_num, u16 edge1) |
388 | { |
389 | u16 edge0 = edge1; |
390 | |
391 | edge0 += (mode->hdisplay + 40) * SUN6I_DSI_TCON_DIV / 8; |
392 | |
393 | if (edge0 > line_num) |
394 | return edge0 - line_num; |
395 | |
396 | return 1; |
397 | } |
398 | |
399 | static u16 sun6i_dsi_get_drq_edge1(struct sun6i_dsi *dsi, |
400 | struct drm_display_mode *mode, |
401 | u16 line_num) |
402 | { |
403 | struct mipi_dsi_device *device = dsi->device; |
404 | unsigned int Bpp = mipi_dsi_pixel_format_to_bpp(fmt: device->format) / 8; |
405 | unsigned int hbp = mode->htotal - mode->hsync_end; |
406 | u16 edge1; |
407 | |
408 | edge1 = SUN6I_DSI_SYNC_POINT; |
409 | edge1 += (mode->hdisplay + hbp + 20) * Bpp / device->lanes; |
410 | |
411 | if (edge1 > line_num) |
412 | return line_num; |
413 | |
414 | return edge1; |
415 | } |
416 | |
417 | static void sun6i_dsi_setup_burst(struct sun6i_dsi *dsi, |
418 | struct drm_display_mode *mode) |
419 | { |
420 | struct mipi_dsi_device *device = dsi->device; |
421 | u32 val = 0; |
422 | |
423 | if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { |
424 | u16 line_num = sun6i_dsi_get_line_num(dsi, mode); |
425 | u16 edge0, edge1; |
426 | |
427 | edge1 = sun6i_dsi_get_drq_edge1(dsi, mode, line_num); |
428 | edge0 = sun6i_dsi_get_drq_edge0(dsi, mode, line_num, edge1); |
429 | |
430 | regmap_write(map: dsi->regs, SUN6I_DSI_BURST_DRQ_REG, |
431 | SUN6I_DSI_BURST_DRQ_EDGE0(edge0) | |
432 | SUN6I_DSI_BURST_DRQ_EDGE1(edge1)); |
433 | |
434 | regmap_write(map: dsi->regs, SUN6I_DSI_BURST_LINE_REG, |
435 | SUN6I_DSI_BURST_LINE_NUM(line_num) | |
436 | SUN6I_DSI_BURST_LINE_SYNC_POINT(SUN6I_DSI_SYNC_POINT)); |
437 | |
438 | val = SUN6I_DSI_TCON_DRQ_ENABLE_MODE; |
439 | } else if ((mode->hsync_start - mode->hdisplay) > 20) { |
440 | /* Maaaaaagic */ |
441 | u16 drq = (mode->hsync_start - mode->hdisplay) - 20; |
442 | |
443 | drq *= mipi_dsi_pixel_format_to_bpp(fmt: device->format); |
444 | drq /= 32; |
445 | |
446 | val = (SUN6I_DSI_TCON_DRQ_ENABLE_MODE | |
447 | SUN6I_DSI_TCON_DRQ_SET(drq)); |
448 | } |
449 | |
450 | regmap_write(map: dsi->regs, SUN6I_DSI_TCON_DRQ_REG, val); |
451 | } |
452 | |
453 | static void sun6i_dsi_setup_inst_loop(struct sun6i_dsi *dsi, |
454 | struct drm_display_mode *mode) |
455 | { |
456 | struct mipi_dsi_device *device = dsi->device; |
457 | u16 delay = 50 - 1; |
458 | |
459 | if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { |
460 | u32 hsync_porch = (mode->htotal - mode->hdisplay) * 150; |
461 | |
462 | delay = (hsync_porch / ((mode->clock / 1000) * 8)); |
463 | delay -= 50; |
464 | } |
465 | |
466 | regmap_write(map: dsi->regs, SUN6I_DSI_INST_LOOP_SEL_REG, |
467 | val: 2 << (4 * DSI_INST_ID_LP11) | |
468 | 3 << (4 * DSI_INST_ID_DLY)); |
469 | |
470 | regmap_write(map: dsi->regs, SUN6I_DSI_INST_LOOP_NUM_REG(0), |
471 | SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) | |
472 | SUN6I_DSI_INST_LOOP_NUM_N1(delay)); |
473 | regmap_write(map: dsi->regs, SUN6I_DSI_INST_LOOP_NUM_REG(1), |
474 | SUN6I_DSI_INST_LOOP_NUM_N0(50 - 1) | |
475 | SUN6I_DSI_INST_LOOP_NUM_N1(delay)); |
476 | } |
477 | |
478 | static void sun6i_dsi_setup_format(struct sun6i_dsi *dsi, |
479 | struct drm_display_mode *mode) |
480 | { |
481 | struct mipi_dsi_device *device = dsi->device; |
482 | u32 val = SUN6I_DSI_PIXEL_PH_VC(device->channel); |
483 | u8 dt, fmt; |
484 | u16 wc; |
485 | |
486 | /* |
487 | * TODO: The format defines are only valid in video mode and |
488 | * change in command mode. |
489 | */ |
490 | switch (device->format) { |
491 | case MIPI_DSI_FMT_RGB888: |
492 | dt = MIPI_DSI_PACKED_PIXEL_STREAM_24; |
493 | fmt = 8; |
494 | break; |
495 | case MIPI_DSI_FMT_RGB666: |
496 | dt = MIPI_DSI_PIXEL_STREAM_3BYTE_18; |
497 | fmt = 9; |
498 | break; |
499 | case MIPI_DSI_FMT_RGB666_PACKED: |
500 | dt = MIPI_DSI_PACKED_PIXEL_STREAM_18; |
501 | fmt = 10; |
502 | break; |
503 | case MIPI_DSI_FMT_RGB565: |
504 | dt = MIPI_DSI_PACKED_PIXEL_STREAM_16; |
505 | fmt = 11; |
506 | break; |
507 | default: |
508 | return; |
509 | } |
510 | val |= SUN6I_DSI_PIXEL_PH_DT(dt); |
511 | |
512 | wc = mode->hdisplay * mipi_dsi_pixel_format_to_bpp(fmt: device->format) / 8; |
513 | val |= SUN6I_DSI_PIXEL_PH_WC(wc); |
514 | val |= SUN6I_DSI_PIXEL_PH_ECC(sun6i_dsi_ecc_compute(val)); |
515 | |
516 | regmap_write(map: dsi->regs, SUN6I_DSI_PIXEL_PH_REG, val); |
517 | |
518 | regmap_write(map: dsi->regs, SUN6I_DSI_PIXEL_PF0_REG, |
519 | SUN6I_DSI_PIXEL_PF0_CRC_FORCE(0xffff)); |
520 | |
521 | regmap_write(map: dsi->regs, SUN6I_DSI_PIXEL_PF1_REG, |
522 | SUN6I_DSI_PIXEL_PF1_CRC_INIT_LINE0(0xffff) | |
523 | SUN6I_DSI_PIXEL_PF1_CRC_INIT_LINEN(0xffff)); |
524 | |
525 | regmap_write(map: dsi->regs, SUN6I_DSI_PIXEL_CTL0_REG, |
526 | SUN6I_DSI_PIXEL_CTL0_PD_PLUG_DISABLE | |
527 | SUN6I_DSI_PIXEL_CTL0_FORMAT(fmt)); |
528 | } |
529 | |
530 | static void sun6i_dsi_setup_timings(struct sun6i_dsi *dsi, |
531 | struct drm_display_mode *mode) |
532 | { |
533 | struct mipi_dsi_device *device = dsi->device; |
534 | int Bpp = mipi_dsi_pixel_format_to_bpp(fmt: device->format) / 8; |
535 | u16 hbp = 0, hfp = 0, hsa = 0, hblk = 0, vblk = 0; |
536 | u32 basic_ctl = 0; |
537 | size_t bytes; |
538 | u8 *buffer; |
539 | |
540 | /* Do all timing calculations up front to allocate buffer space */ |
541 | |
542 | if (device->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) { |
543 | hblk = mode->hdisplay * Bpp; |
544 | basic_ctl = SUN6I_DSI_BASIC_CTL_VIDEO_BURST | |
545 | SUN6I_DSI_BASIC_CTL_HSA_HSE_DIS | |
546 | SUN6I_DSI_BASIC_CTL_HBP_DIS; |
547 | |
548 | if (device->lanes == 4) |
549 | basic_ctl |= SUN6I_DSI_BASIC_CTL_TRAIL_FILL | |
550 | SUN6I_DSI_BASIC_CTL_TRAIL_INV(0xc); |
551 | } else { |
552 | /* |
553 | * A sync period is composed of a blanking packet (4 |
554 | * bytes + payload + 2 bytes) and a sync event packet |
555 | * (4 bytes). Its minimal size is therefore 10 bytes |
556 | */ |
557 | #define HSA_PACKET_OVERHEAD 10 |
558 | hsa = max(HSA_PACKET_OVERHEAD, |
559 | (mode->hsync_end - mode->hsync_start) * Bpp - HSA_PACKET_OVERHEAD); |
560 | |
561 | /* |
562 | * The backporch is set using a blanking packet (4 |
563 | * bytes + payload + 2 bytes). Its minimal size is |
564 | * therefore 6 bytes |
565 | */ |
566 | #define HBP_PACKET_OVERHEAD 6 |
567 | hbp = max(HBP_PACKET_OVERHEAD, |
568 | (mode->htotal - mode->hsync_end) * Bpp - HBP_PACKET_OVERHEAD); |
569 | |
570 | /* |
571 | * The frontporch is set using a sync event (4 bytes) |
572 | * and two blanking packets (each one is 4 bytes + |
573 | * payload + 2 bytes). Its minimal size is therefore |
574 | * 16 bytes |
575 | */ |
576 | #define HFP_PACKET_OVERHEAD 16 |
577 | hfp = max(HFP_PACKET_OVERHEAD, |
578 | (mode->hsync_start - mode->hdisplay) * Bpp - HFP_PACKET_OVERHEAD); |
579 | |
580 | /* |
581 | * The blanking is set using a sync event (4 bytes) |
582 | * and a blanking packet (4 bytes + payload + 2 |
583 | * bytes). Its minimal size is therefore 10 bytes. |
584 | */ |
585 | #define HBLK_PACKET_OVERHEAD 10 |
586 | hblk = max(HBLK_PACKET_OVERHEAD, |
587 | (mode->htotal - (mode->hsync_end - mode->hsync_start)) * Bpp - |
588 | HBLK_PACKET_OVERHEAD); |
589 | |
590 | /* |
591 | * And I'm not entirely sure what vblk is about. The driver in |
592 | * Allwinner BSP is using a rather convoluted calculation |
593 | * there only for 4 lanes. However, using 0 (the !4 lanes |
594 | * case) even with a 4 lanes screen seems to work... |
595 | */ |
596 | vblk = 0; |
597 | } |
598 | |
599 | /* How many bytes do we need to send all payloads? */ |
600 | bytes = max_t(size_t, max(max(hfp, hblk), max(hsa, hbp)), vblk); |
601 | buffer = kmalloc(size: bytes, GFP_KERNEL); |
602 | if (WARN_ON(!buffer)) |
603 | return; |
604 | |
605 | regmap_write(map: dsi->regs, SUN6I_DSI_BASIC_CTL_REG, val: basic_ctl); |
606 | |
607 | regmap_write(map: dsi->regs, SUN6I_DSI_SYNC_HSS_REG, |
608 | val: sun6i_dsi_build_sync_pkt(dt: MIPI_DSI_H_SYNC_START, |
609 | vc: device->channel, |
610 | d0: 0, d1: 0)); |
611 | |
612 | regmap_write(map: dsi->regs, SUN6I_DSI_SYNC_HSE_REG, |
613 | val: sun6i_dsi_build_sync_pkt(dt: MIPI_DSI_H_SYNC_END, |
614 | vc: device->channel, |
615 | d0: 0, d1: 0)); |
616 | |
617 | regmap_write(map: dsi->regs, SUN6I_DSI_SYNC_VSS_REG, |
618 | val: sun6i_dsi_build_sync_pkt(dt: MIPI_DSI_V_SYNC_START, |
619 | vc: device->channel, |
620 | d0: 0, d1: 0)); |
621 | |
622 | regmap_write(map: dsi->regs, SUN6I_DSI_SYNC_VSE_REG, |
623 | val: sun6i_dsi_build_sync_pkt(dt: MIPI_DSI_V_SYNC_END, |
624 | vc: device->channel, |
625 | d0: 0, d1: 0)); |
626 | |
627 | regmap_write(map: dsi->regs, SUN6I_DSI_BASIC_SIZE0_REG, |
628 | SUN6I_DSI_BASIC_SIZE0_VSA(mode->vsync_end - |
629 | mode->vsync_start) | |
630 | SUN6I_DSI_BASIC_SIZE0_VBP(mode->vtotal - |
631 | mode->vsync_end)); |
632 | |
633 | regmap_write(map: dsi->regs, SUN6I_DSI_BASIC_SIZE1_REG, |
634 | SUN6I_DSI_BASIC_SIZE1_VACT(mode->vdisplay) | |
635 | SUN6I_DSI_BASIC_SIZE1_VT(mode->vtotal)); |
636 | |
637 | /* sync */ |
638 | regmap_write(map: dsi->regs, SUN6I_DSI_BLK_HSA0_REG, |
639 | val: sun6i_dsi_build_blk0_pkt(vc: device->channel, wc: hsa)); |
640 | regmap_write(map: dsi->regs, SUN6I_DSI_BLK_HSA1_REG, |
641 | val: sun6i_dsi_build_blk1_pkt(pd: 0, buffer, len: hsa)); |
642 | |
643 | /* backporch */ |
644 | regmap_write(map: dsi->regs, SUN6I_DSI_BLK_HBP0_REG, |
645 | val: sun6i_dsi_build_blk0_pkt(vc: device->channel, wc: hbp)); |
646 | regmap_write(map: dsi->regs, SUN6I_DSI_BLK_HBP1_REG, |
647 | val: sun6i_dsi_build_blk1_pkt(pd: 0, buffer, len: hbp)); |
648 | |
649 | /* frontporch */ |
650 | regmap_write(map: dsi->regs, SUN6I_DSI_BLK_HFP0_REG, |
651 | val: sun6i_dsi_build_blk0_pkt(vc: device->channel, wc: hfp)); |
652 | regmap_write(map: dsi->regs, SUN6I_DSI_BLK_HFP1_REG, |
653 | val: sun6i_dsi_build_blk1_pkt(pd: 0, buffer, len: hfp)); |
654 | |
655 | /* hblk */ |
656 | regmap_write(map: dsi->regs, SUN6I_DSI_BLK_HBLK0_REG, |
657 | val: sun6i_dsi_build_blk0_pkt(vc: device->channel, wc: hblk)); |
658 | regmap_write(map: dsi->regs, SUN6I_DSI_BLK_HBLK1_REG, |
659 | val: sun6i_dsi_build_blk1_pkt(pd: 0, buffer, len: hblk)); |
660 | |
661 | /* vblk */ |
662 | regmap_write(map: dsi->regs, SUN6I_DSI_BLK_VBLK0_REG, |
663 | val: sun6i_dsi_build_blk0_pkt(vc: device->channel, wc: vblk)); |
664 | regmap_write(map: dsi->regs, SUN6I_DSI_BLK_VBLK1_REG, |
665 | val: sun6i_dsi_build_blk1_pkt(pd: 0, buffer, len: vblk)); |
666 | |
667 | kfree(objp: buffer); |
668 | } |
669 | |
670 | static int sun6i_dsi_start(struct sun6i_dsi *dsi, |
671 | enum sun6i_dsi_start_inst func) |
672 | { |
673 | switch (func) { |
674 | case DSI_START_LPTX: |
675 | regmap_write(map: dsi->regs, SUN6I_DSI_INST_JUMP_SEL_REG, |
676 | val: DSI_INST_ID_LPDT << (4 * DSI_INST_ID_LP11) | |
677 | DSI_INST_ID_END << (4 * DSI_INST_ID_LPDT)); |
678 | break; |
679 | case DSI_START_LPRX: |
680 | regmap_write(map: dsi->regs, SUN6I_DSI_INST_JUMP_SEL_REG, |
681 | val: DSI_INST_ID_LPDT << (4 * DSI_INST_ID_LP11) | |
682 | DSI_INST_ID_DLY << (4 * DSI_INST_ID_LPDT) | |
683 | DSI_INST_ID_TBA << (4 * DSI_INST_ID_DLY) | |
684 | DSI_INST_ID_END << (4 * DSI_INST_ID_TBA)); |
685 | break; |
686 | case DSI_START_HSC: |
687 | regmap_write(map: dsi->regs, SUN6I_DSI_INST_JUMP_SEL_REG, |
688 | val: DSI_INST_ID_HSC << (4 * DSI_INST_ID_LP11) | |
689 | DSI_INST_ID_END << (4 * DSI_INST_ID_HSC)); |
690 | break; |
691 | case DSI_START_HSD: |
692 | regmap_write(map: dsi->regs, SUN6I_DSI_INST_JUMP_SEL_REG, |
693 | val: DSI_INST_ID_NOP << (4 * DSI_INST_ID_LP11) | |
694 | DSI_INST_ID_HSD << (4 * DSI_INST_ID_NOP) | |
695 | DSI_INST_ID_DLY << (4 * DSI_INST_ID_HSD) | |
696 | DSI_INST_ID_NOP << (4 * DSI_INST_ID_DLY) | |
697 | DSI_INST_ID_END << (4 * DSI_INST_ID_HSCEXIT)); |
698 | break; |
699 | default: |
700 | regmap_write(map: dsi->regs, SUN6I_DSI_INST_JUMP_SEL_REG, |
701 | val: DSI_INST_ID_END << (4 * DSI_INST_ID_LP11)); |
702 | break; |
703 | } |
704 | |
705 | sun6i_dsi_inst_abort(dsi); |
706 | sun6i_dsi_inst_commit(dsi); |
707 | |
708 | if (func == DSI_START_HSC) |
709 | regmap_write_bits(map: dsi->regs, |
710 | SUN6I_DSI_INST_FUNC_REG(DSI_INST_ID_LP11), |
711 | SUN6I_DSI_INST_FUNC_LANE_CEN, val: 0); |
712 | |
713 | return 0; |
714 | } |
715 | |
716 | static void sun6i_dsi_encoder_enable(struct drm_encoder *encoder) |
717 | { |
718 | struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; |
719 | struct sun6i_dsi *dsi = encoder_to_sun6i_dsi(encoder); |
720 | struct mipi_dsi_device *device = dsi->device; |
721 | union phy_configure_opts opts = { }; |
722 | struct phy_configure_opts_mipi_dphy *cfg = &opts.mipi_dphy; |
723 | u16 delay; |
724 | int err; |
725 | |
726 | DRM_DEBUG_DRIVER("Enabling DSI output\n" ); |
727 | |
728 | err = regulator_enable(regulator: dsi->regulator); |
729 | if (err) |
730 | dev_warn(dsi->dev, "failed to enable VCC-DSI supply: %d\n" , err); |
731 | |
732 | reset_control_deassert(rstc: dsi->reset); |
733 | clk_prepare_enable(clk: dsi->mod_clk); |
734 | |
735 | /* |
736 | * Enable the DSI block. |
737 | */ |
738 | regmap_write(map: dsi->regs, SUN6I_DSI_CTL_REG, SUN6I_DSI_CTL_EN); |
739 | |
740 | regmap_write(map: dsi->regs, SUN6I_DSI_BASIC_CTL0_REG, |
741 | SUN6I_DSI_BASIC_CTL0_ECC_EN | SUN6I_DSI_BASIC_CTL0_CRC_EN); |
742 | |
743 | regmap_write(map: dsi->regs, SUN6I_DSI_TRANS_START_REG, val: 10); |
744 | regmap_write(map: dsi->regs, SUN6I_DSI_TRANS_ZERO_REG, val: 0); |
745 | |
746 | sun6i_dsi_inst_init(dsi, device: dsi->device); |
747 | |
748 | regmap_write(map: dsi->regs, SUN6I_DSI_DEBUG_DATA_REG, val: 0xff); |
749 | |
750 | delay = sun6i_dsi_get_video_start_delay(dsi, mode); |
751 | regmap_write(map: dsi->regs, SUN6I_DSI_BASIC_CTL1_REG, |
752 | SUN6I_DSI_BASIC_CTL1_VIDEO_ST_DELAY(delay) | |
753 | SUN6I_DSI_BASIC_CTL1_VIDEO_FILL | |
754 | SUN6I_DSI_BASIC_CTL1_VIDEO_PRECISION | |
755 | SUN6I_DSI_BASIC_CTL1_VIDEO_MODE); |
756 | |
757 | sun6i_dsi_setup_burst(dsi, mode); |
758 | sun6i_dsi_setup_inst_loop(dsi, mode); |
759 | sun6i_dsi_setup_format(dsi, mode); |
760 | sun6i_dsi_setup_timings(dsi, mode); |
761 | |
762 | phy_init(phy: dsi->dphy); |
763 | |
764 | phy_mipi_dphy_get_default_config(pixel_clock: mode->clock * 1000, |
765 | bpp: mipi_dsi_pixel_format_to_bpp(fmt: device->format), |
766 | lanes: device->lanes, cfg); |
767 | |
768 | phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY); |
769 | phy_configure(phy: dsi->dphy, opts: &opts); |
770 | phy_power_on(phy: dsi->dphy); |
771 | |
772 | if (dsi->panel) |
773 | drm_panel_prepare(panel: dsi->panel); |
774 | |
775 | /* |
776 | * FIXME: This should be moved after the switch to HS mode. |
777 | * |
778 | * Unfortunately, once in HS mode, it seems like we're not |
779 | * able to send DCS commands anymore, which would prevent any |
780 | * panel to send any DCS command as part as their enable |
781 | * method, which is quite common. |
782 | * |
783 | * I haven't seen any artifact due to that sub-optimal |
784 | * ordering on the panels I've tested it with, so I guess this |
785 | * will do for now, until that IP is better understood. |
786 | */ |
787 | if (dsi->panel) |
788 | drm_panel_enable(panel: dsi->panel); |
789 | |
790 | sun6i_dsi_start(dsi, func: DSI_START_HSC); |
791 | |
792 | udelay(1000); |
793 | |
794 | sun6i_dsi_start(dsi, func: DSI_START_HSD); |
795 | } |
796 | |
797 | static void sun6i_dsi_encoder_disable(struct drm_encoder *encoder) |
798 | { |
799 | struct sun6i_dsi *dsi = encoder_to_sun6i_dsi(encoder); |
800 | |
801 | DRM_DEBUG_DRIVER("Disabling DSI output\n" ); |
802 | |
803 | if (dsi->panel) { |
804 | drm_panel_disable(panel: dsi->panel); |
805 | drm_panel_unprepare(panel: dsi->panel); |
806 | } |
807 | |
808 | phy_power_off(phy: dsi->dphy); |
809 | phy_exit(phy: dsi->dphy); |
810 | |
811 | clk_disable_unprepare(clk: dsi->mod_clk); |
812 | reset_control_assert(rstc: dsi->reset); |
813 | regulator_disable(regulator: dsi->regulator); |
814 | } |
815 | |
816 | static int sun6i_dsi_get_modes(struct drm_connector *connector) |
817 | { |
818 | struct sun6i_dsi *dsi = connector_to_sun6i_dsi(connector); |
819 | |
820 | return drm_panel_get_modes(panel: dsi->panel, connector); |
821 | } |
822 | |
823 | static const struct drm_connector_helper_funcs sun6i_dsi_connector_helper_funcs = { |
824 | .get_modes = sun6i_dsi_get_modes, |
825 | }; |
826 | |
827 | static enum drm_connector_status |
828 | sun6i_dsi_connector_detect(struct drm_connector *connector, bool force) |
829 | { |
830 | struct sun6i_dsi *dsi = connector_to_sun6i_dsi(connector); |
831 | |
832 | return dsi->panel ? connector_status_connected : |
833 | connector_status_disconnected; |
834 | } |
835 | |
836 | static const struct drm_connector_funcs sun6i_dsi_connector_funcs = { |
837 | .detect = sun6i_dsi_connector_detect, |
838 | .fill_modes = drm_helper_probe_single_connector_modes, |
839 | .destroy = drm_connector_cleanup, |
840 | .reset = drm_atomic_helper_connector_reset, |
841 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, |
842 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, |
843 | }; |
844 | |
845 | static const struct drm_encoder_helper_funcs sun6i_dsi_enc_helper_funcs = { |
846 | .disable = sun6i_dsi_encoder_disable, |
847 | .enable = sun6i_dsi_encoder_enable, |
848 | }; |
849 | |
850 | static u32 sun6i_dsi_dcs_build_pkt_hdr(struct sun6i_dsi *dsi, |
851 | const struct mipi_dsi_msg *msg) |
852 | { |
853 | u32 pkt = msg->type; |
854 | |
855 | if (msg->type == MIPI_DSI_DCS_LONG_WRITE) { |
856 | pkt |= ((msg->tx_len) & 0xffff) << 8; |
857 | pkt |= (((msg->tx_len) >> 8) & 0xffff) << 16; |
858 | } else { |
859 | pkt |= (((u8 *)msg->tx_buf)[0] << 8); |
860 | if (msg->tx_len > 1) |
861 | pkt |= (((u8 *)msg->tx_buf)[1] << 16); |
862 | } |
863 | |
864 | pkt |= sun6i_dsi_ecc_compute(data: pkt) << 24; |
865 | |
866 | return pkt; |
867 | } |
868 | |
869 | static int sun6i_dsi_dcs_write_short(struct sun6i_dsi *dsi, |
870 | const struct mipi_dsi_msg *msg) |
871 | { |
872 | regmap_write(map: dsi->regs, SUN6I_DSI_CMD_TX_REG(0), |
873 | val: sun6i_dsi_dcs_build_pkt_hdr(dsi, msg)); |
874 | regmap_write_bits(map: dsi->regs, SUN6I_DSI_CMD_CTL_REG, |
875 | mask: 0xff, val: (4 - 1)); |
876 | |
877 | sun6i_dsi_start(dsi, func: DSI_START_LPTX); |
878 | |
879 | return msg->tx_len; |
880 | } |
881 | |
882 | static int sun6i_dsi_dcs_write_long(struct sun6i_dsi *dsi, |
883 | const struct mipi_dsi_msg *msg) |
884 | { |
885 | int ret, len = 0; |
886 | u8 *bounce; |
887 | u16 crc; |
888 | |
889 | regmap_write(map: dsi->regs, SUN6I_DSI_CMD_TX_REG(0), |
890 | val: sun6i_dsi_dcs_build_pkt_hdr(dsi, msg)); |
891 | |
892 | bounce = kzalloc(ALIGN(msg->tx_len + sizeof(crc), 4), GFP_KERNEL); |
893 | if (!bounce) |
894 | return -ENOMEM; |
895 | |
896 | memcpy(bounce, msg->tx_buf, msg->tx_len); |
897 | len += msg->tx_len; |
898 | |
899 | crc = sun6i_dsi_crc_compute(buffer: bounce, len: msg->tx_len); |
900 | memcpy((u8 *)bounce + msg->tx_len, &crc, sizeof(crc)); |
901 | len += sizeof(crc); |
902 | |
903 | regmap_bulk_write(map: dsi->regs, SUN6I_DSI_CMD_TX_REG(1), val: bounce, DIV_ROUND_UP(len, 4)); |
904 | regmap_write(map: dsi->regs, SUN6I_DSI_CMD_CTL_REG, val: len + 4 - 1); |
905 | kfree(objp: bounce); |
906 | |
907 | sun6i_dsi_start(dsi, func: DSI_START_LPTX); |
908 | |
909 | ret = sun6i_dsi_inst_wait_for_completion(dsi); |
910 | if (ret < 0) { |
911 | sun6i_dsi_inst_abort(dsi); |
912 | return ret; |
913 | } |
914 | |
915 | /* |
916 | * TODO: There's some bits (reg 0x200, bits 8/9) that |
917 | * apparently can be used to check whether the data have been |
918 | * sent, but I couldn't get it to work reliably. |
919 | */ |
920 | return msg->tx_len; |
921 | } |
922 | |
923 | static int sun6i_dsi_dcs_read(struct sun6i_dsi *dsi, |
924 | const struct mipi_dsi_msg *msg) |
925 | { |
926 | u32 val; |
927 | int ret; |
928 | u8 byte0; |
929 | |
930 | regmap_write(map: dsi->regs, SUN6I_DSI_CMD_TX_REG(0), |
931 | val: sun6i_dsi_dcs_build_pkt_hdr(dsi, msg)); |
932 | regmap_write(map: dsi->regs, SUN6I_DSI_CMD_CTL_REG, |
933 | val: (4 - 1)); |
934 | |
935 | sun6i_dsi_start(dsi, func: DSI_START_LPRX); |
936 | |
937 | ret = sun6i_dsi_inst_wait_for_completion(dsi); |
938 | if (ret < 0) { |
939 | sun6i_dsi_inst_abort(dsi); |
940 | return ret; |
941 | } |
942 | |
943 | /* |
944 | * TODO: There's some bits (reg 0x200, bits 24/25) that |
945 | * apparently can be used to check whether the data have been |
946 | * received, but I couldn't get it to work reliably. |
947 | */ |
948 | regmap_read(map: dsi->regs, SUN6I_DSI_CMD_CTL_REG, val: &val); |
949 | if (val & SUN6I_DSI_CMD_CTL_RX_OVERFLOW) |
950 | return -EIO; |
951 | |
952 | regmap_read(map: dsi->regs, SUN6I_DSI_CMD_RX_REG(0), val: &val); |
953 | byte0 = val & 0xff; |
954 | if (byte0 == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) |
955 | return -EIO; |
956 | |
957 | ((u8 *)msg->rx_buf)[0] = (val >> 8); |
958 | |
959 | return 1; |
960 | } |
961 | |
962 | static int sun6i_dsi_attach(struct mipi_dsi_host *host, |
963 | struct mipi_dsi_device *device) |
964 | { |
965 | struct sun6i_dsi *dsi = host_to_sun6i_dsi(host); |
966 | struct drm_panel *panel = of_drm_find_panel(np: device->dev.of_node); |
967 | |
968 | if (IS_ERR(ptr: panel)) |
969 | return PTR_ERR(ptr: panel); |
970 | if (!dsi->drm || !dsi->drm->registered) |
971 | return -EPROBE_DEFER; |
972 | |
973 | dsi->panel = panel; |
974 | dsi->device = device; |
975 | |
976 | drm_kms_helper_hotplug_event(dev: dsi->drm); |
977 | |
978 | dev_info(host->dev, "Attached device %s\n" , device->name); |
979 | |
980 | return 0; |
981 | } |
982 | |
983 | static int sun6i_dsi_detach(struct mipi_dsi_host *host, |
984 | struct mipi_dsi_device *device) |
985 | { |
986 | struct sun6i_dsi *dsi = host_to_sun6i_dsi(host); |
987 | |
988 | dsi->panel = NULL; |
989 | dsi->device = NULL; |
990 | |
991 | drm_kms_helper_hotplug_event(dev: dsi->drm); |
992 | |
993 | return 0; |
994 | } |
995 | |
996 | static ssize_t sun6i_dsi_transfer(struct mipi_dsi_host *host, |
997 | const struct mipi_dsi_msg *msg) |
998 | { |
999 | struct sun6i_dsi *dsi = host_to_sun6i_dsi(host); |
1000 | int ret; |
1001 | |
1002 | ret = sun6i_dsi_inst_wait_for_completion(dsi); |
1003 | if (ret < 0) |
1004 | sun6i_dsi_inst_abort(dsi); |
1005 | |
1006 | regmap_write(map: dsi->regs, SUN6I_DSI_CMD_CTL_REG, |
1007 | SUN6I_DSI_CMD_CTL_RX_OVERFLOW | |
1008 | SUN6I_DSI_CMD_CTL_RX_FLAG | |
1009 | SUN6I_DSI_CMD_CTL_TX_FLAG); |
1010 | |
1011 | switch (msg->type) { |
1012 | case MIPI_DSI_DCS_SHORT_WRITE: |
1013 | case MIPI_DSI_DCS_SHORT_WRITE_PARAM: |
1014 | case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: |
1015 | ret = sun6i_dsi_dcs_write_short(dsi, msg); |
1016 | break; |
1017 | |
1018 | case MIPI_DSI_DCS_LONG_WRITE: |
1019 | ret = sun6i_dsi_dcs_write_long(dsi, msg); |
1020 | break; |
1021 | |
1022 | case MIPI_DSI_DCS_READ: |
1023 | if (msg->rx_len == 1) { |
1024 | ret = sun6i_dsi_dcs_read(dsi, msg); |
1025 | break; |
1026 | } |
1027 | fallthrough; |
1028 | |
1029 | default: |
1030 | ret = -EINVAL; |
1031 | } |
1032 | |
1033 | return ret; |
1034 | } |
1035 | |
1036 | static const struct mipi_dsi_host_ops sun6i_dsi_host_ops = { |
1037 | .attach = sun6i_dsi_attach, |
1038 | .detach = sun6i_dsi_detach, |
1039 | .transfer = sun6i_dsi_transfer, |
1040 | }; |
1041 | |
1042 | static const struct regmap_config sun6i_dsi_regmap_config = { |
1043 | .reg_bits = 32, |
1044 | .val_bits = 32, |
1045 | .reg_stride = 4, |
1046 | .max_register = SUN6I_DSI_CMD_TX_REG(255), |
1047 | .name = "mipi-dsi" , |
1048 | }; |
1049 | |
1050 | static int sun6i_dsi_bind(struct device *dev, struct device *master, |
1051 | void *data) |
1052 | { |
1053 | struct drm_device *drm = data; |
1054 | struct sun6i_dsi *dsi = dev_get_drvdata(dev); |
1055 | int ret; |
1056 | |
1057 | drm_encoder_helper_add(encoder: &dsi->encoder, |
1058 | funcs: &sun6i_dsi_enc_helper_funcs); |
1059 | ret = drm_simple_encoder_init(dev: drm, encoder: &dsi->encoder, |
1060 | DRM_MODE_ENCODER_DSI); |
1061 | if (ret) { |
1062 | dev_err(dsi->dev, "Couldn't initialise the DSI encoder\n" ); |
1063 | return ret; |
1064 | } |
1065 | dsi->encoder.possible_crtcs = BIT(0); |
1066 | |
1067 | drm_connector_helper_add(connector: &dsi->connector, |
1068 | funcs: &sun6i_dsi_connector_helper_funcs); |
1069 | ret = drm_connector_init(dev: drm, connector: &dsi->connector, |
1070 | funcs: &sun6i_dsi_connector_funcs, |
1071 | DRM_MODE_CONNECTOR_DSI); |
1072 | if (ret) { |
1073 | dev_err(dsi->dev, |
1074 | "Couldn't initialise the DSI connector\n" ); |
1075 | goto err_cleanup_connector; |
1076 | } |
1077 | |
1078 | drm_connector_attach_encoder(connector: &dsi->connector, encoder: &dsi->encoder); |
1079 | |
1080 | dsi->drm = drm; |
1081 | |
1082 | return 0; |
1083 | |
1084 | err_cleanup_connector: |
1085 | drm_encoder_cleanup(encoder: &dsi->encoder); |
1086 | return ret; |
1087 | } |
1088 | |
1089 | static void sun6i_dsi_unbind(struct device *dev, struct device *master, |
1090 | void *data) |
1091 | { |
1092 | struct sun6i_dsi *dsi = dev_get_drvdata(dev); |
1093 | |
1094 | dsi->drm = NULL; |
1095 | } |
1096 | |
1097 | static const struct component_ops sun6i_dsi_ops = { |
1098 | .bind = sun6i_dsi_bind, |
1099 | .unbind = sun6i_dsi_unbind, |
1100 | }; |
1101 | |
1102 | static int sun6i_dsi_probe(struct platform_device *pdev) |
1103 | { |
1104 | const struct sun6i_dsi_variant *variant; |
1105 | struct device *dev = &pdev->dev; |
1106 | struct sun6i_dsi *dsi; |
1107 | void __iomem *base; |
1108 | int ret; |
1109 | |
1110 | variant = device_get_match_data(dev); |
1111 | if (!variant) |
1112 | return -EINVAL; |
1113 | |
1114 | dsi = devm_kzalloc(dev, size: sizeof(*dsi), GFP_KERNEL); |
1115 | if (!dsi) |
1116 | return -ENOMEM; |
1117 | dev_set_drvdata(dev, data: dsi); |
1118 | dsi->dev = dev; |
1119 | dsi->host.ops = &sun6i_dsi_host_ops; |
1120 | dsi->host.dev = dev; |
1121 | dsi->variant = variant; |
1122 | |
1123 | base = devm_platform_ioremap_resource(pdev, index: 0); |
1124 | if (IS_ERR(ptr: base)) { |
1125 | dev_err(dev, "Couldn't map the DSI encoder registers\n" ); |
1126 | return PTR_ERR(ptr: base); |
1127 | } |
1128 | |
1129 | dsi->regulator = devm_regulator_get(dev, id: "vcc-dsi" ); |
1130 | if (IS_ERR(ptr: dsi->regulator)) |
1131 | return dev_err_probe(dev, err: PTR_ERR(ptr: dsi->regulator), |
1132 | fmt: "Couldn't get VCC-DSI supply\n" ); |
1133 | |
1134 | dsi->reset = devm_reset_control_get_shared(dev, NULL); |
1135 | if (IS_ERR(ptr: dsi->reset)) { |
1136 | dev_err(dev, "Couldn't get our reset line\n" ); |
1137 | return PTR_ERR(ptr: dsi->reset); |
1138 | } |
1139 | |
1140 | dsi->regs = devm_regmap_init_mmio(dev, base, &sun6i_dsi_regmap_config); |
1141 | if (IS_ERR(ptr: dsi->regs)) { |
1142 | dev_err(dev, "Couldn't init regmap\n" ); |
1143 | return PTR_ERR(ptr: dsi->regs); |
1144 | } |
1145 | |
1146 | dsi->bus_clk = devm_clk_get(dev, id: variant->has_mod_clk ? "bus" : NULL); |
1147 | if (IS_ERR(ptr: dsi->bus_clk)) |
1148 | return dev_err_probe(dev, err: PTR_ERR(ptr: dsi->bus_clk), |
1149 | fmt: "Couldn't get the DSI bus clock\n" ); |
1150 | |
1151 | ret = regmap_mmio_attach_clk(map: dsi->regs, clk: dsi->bus_clk); |
1152 | if (ret) |
1153 | return ret; |
1154 | |
1155 | if (variant->has_mod_clk) { |
1156 | dsi->mod_clk = devm_clk_get(dev, id: "mod" ); |
1157 | if (IS_ERR(ptr: dsi->mod_clk)) { |
1158 | dev_err(dev, "Couldn't get the DSI mod clock\n" ); |
1159 | ret = PTR_ERR(ptr: dsi->mod_clk); |
1160 | goto err_attach_clk; |
1161 | } |
1162 | |
1163 | /* |
1164 | * In order to operate properly, the module clock on the |
1165 | * A31 variant always seems to be set to 297MHz. |
1166 | */ |
1167 | if (variant->set_mod_clk) |
1168 | clk_set_rate_exclusive(clk: dsi->mod_clk, rate: 297000000); |
1169 | } |
1170 | |
1171 | dsi->dphy = devm_phy_get(dev, string: "dphy" ); |
1172 | if (IS_ERR(ptr: dsi->dphy)) { |
1173 | dev_err(dev, "Couldn't get the MIPI D-PHY\n" ); |
1174 | ret = PTR_ERR(ptr: dsi->dphy); |
1175 | goto err_unprotect_clk; |
1176 | } |
1177 | |
1178 | ret = mipi_dsi_host_register(host: &dsi->host); |
1179 | if (ret) { |
1180 | dev_err(dev, "Couldn't register MIPI-DSI host\n" ); |
1181 | goto err_unprotect_clk; |
1182 | } |
1183 | |
1184 | ret = component_add(&pdev->dev, &sun6i_dsi_ops); |
1185 | if (ret) { |
1186 | dev_err(dev, "Couldn't register our component\n" ); |
1187 | goto err_remove_dsi_host; |
1188 | } |
1189 | |
1190 | return 0; |
1191 | |
1192 | err_remove_dsi_host: |
1193 | mipi_dsi_host_unregister(host: &dsi->host); |
1194 | err_unprotect_clk: |
1195 | if (dsi->variant->has_mod_clk && dsi->variant->set_mod_clk) |
1196 | clk_rate_exclusive_put(clk: dsi->mod_clk); |
1197 | err_attach_clk: |
1198 | regmap_mmio_detach_clk(map: dsi->regs); |
1199 | |
1200 | return ret; |
1201 | } |
1202 | |
1203 | static void sun6i_dsi_remove(struct platform_device *pdev) |
1204 | { |
1205 | struct device *dev = &pdev->dev; |
1206 | struct sun6i_dsi *dsi = dev_get_drvdata(dev); |
1207 | |
1208 | component_del(&pdev->dev, &sun6i_dsi_ops); |
1209 | mipi_dsi_host_unregister(host: &dsi->host); |
1210 | if (dsi->variant->has_mod_clk && dsi->variant->set_mod_clk) |
1211 | clk_rate_exclusive_put(clk: dsi->mod_clk); |
1212 | |
1213 | regmap_mmio_detach_clk(map: dsi->regs); |
1214 | } |
1215 | |
1216 | static const struct sun6i_dsi_variant sun6i_a31_mipi_dsi_variant = { |
1217 | .has_mod_clk = true, |
1218 | .set_mod_clk = true, |
1219 | }; |
1220 | |
1221 | static const struct sun6i_dsi_variant sun50i_a64_mipi_dsi_variant = { |
1222 | }; |
1223 | |
1224 | static const struct sun6i_dsi_variant sun50i_a100_mipi_dsi_variant = { |
1225 | .has_mod_clk = true, |
1226 | }; |
1227 | |
1228 | static const struct of_device_id sun6i_dsi_of_table[] = { |
1229 | { |
1230 | .compatible = "allwinner,sun6i-a31-mipi-dsi" , |
1231 | .data = &sun6i_a31_mipi_dsi_variant, |
1232 | }, |
1233 | { |
1234 | .compatible = "allwinner,sun50i-a64-mipi-dsi" , |
1235 | .data = &sun50i_a64_mipi_dsi_variant, |
1236 | }, |
1237 | { |
1238 | .compatible = "allwinner,sun50i-a100-mipi-dsi" , |
1239 | .data = &sun50i_a100_mipi_dsi_variant, |
1240 | }, |
1241 | { } |
1242 | }; |
1243 | MODULE_DEVICE_TABLE(of, sun6i_dsi_of_table); |
1244 | |
1245 | static struct platform_driver sun6i_dsi_platform_driver = { |
1246 | .probe = sun6i_dsi_probe, |
1247 | .remove_new = sun6i_dsi_remove, |
1248 | .driver = { |
1249 | .name = "sun6i-mipi-dsi" , |
1250 | .of_match_table = sun6i_dsi_of_table, |
1251 | }, |
1252 | }; |
1253 | module_platform_driver(sun6i_dsi_platform_driver); |
1254 | |
1255 | MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>" ); |
1256 | MODULE_DESCRIPTION("Allwinner A31 DSI Driver" ); |
1257 | MODULE_LICENSE("GPL" ); |
1258 | |