1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright (C) 2023 Loongson Technology Corporation Limited |
4 | */ |
5 | |
6 | #include <linux/delay.h> |
7 | |
8 | #include <drm/drm_atomic.h> |
9 | #include <drm/drm_atomic_helper.h> |
10 | #include <drm/drm_debugfs.h> |
11 | #include <drm/drm_vblank.h> |
12 | |
13 | #include "lsdc_drv.h" |
14 | |
15 | /* |
16 | * After the CRTC soft reset, the vblank counter would be reset to zero. |
17 | * But the address and other settings in the CRTC register remain the same |
18 | * as before. |
19 | */ |
20 | |
21 | static void lsdc_crtc0_soft_reset(struct lsdc_crtc *lcrtc) |
22 | { |
23 | struct lsdc_device *ldev = lcrtc->ldev; |
24 | u32 val; |
25 | |
26 | val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); |
27 | |
28 | val &= CFG_VALID_BITS_MASK; |
29 | |
30 | /* Soft reset bit, active low */ |
31 | val &= ~CFG_RESET_N; |
32 | |
33 | val &= ~CFG_PIX_FMT_MASK; |
34 | |
35 | lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val); |
36 | |
37 | udelay(1); |
38 | |
39 | val |= CFG_RESET_N | LSDC_PF_XRGB8888 | CFG_OUTPUT_ENABLE; |
40 | |
41 | lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val); |
42 | |
43 | /* Wait about a vblank time */ |
44 | mdelay(20); |
45 | } |
46 | |
47 | static void lsdc_crtc1_soft_reset(struct lsdc_crtc *lcrtc) |
48 | { |
49 | struct lsdc_device *ldev = lcrtc->ldev; |
50 | u32 val; |
51 | |
52 | val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); |
53 | |
54 | val &= CFG_VALID_BITS_MASK; |
55 | |
56 | /* Soft reset bit, active low */ |
57 | val &= ~CFG_RESET_N; |
58 | |
59 | val &= ~CFG_PIX_FMT_MASK; |
60 | |
61 | lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val); |
62 | |
63 | udelay(1); |
64 | |
65 | val |= CFG_RESET_N | LSDC_PF_XRGB8888 | CFG_OUTPUT_ENABLE; |
66 | |
67 | lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val); |
68 | |
69 | /* Wait about a vblank time */ |
70 | msleep(msecs: 20); |
71 | } |
72 | |
73 | static void lsdc_crtc0_enable(struct lsdc_crtc *lcrtc) |
74 | { |
75 | struct lsdc_device *ldev = lcrtc->ldev; |
76 | u32 val; |
77 | |
78 | val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); |
79 | |
80 | /* |
81 | * This may happen in extremely rare cases, but a soft reset can |
82 | * bring it back to normal. We add a warning here, hoping to catch |
83 | * something if it happens. |
84 | */ |
85 | if (val & CRTC_ANCHORED) { |
86 | drm_warn(&ldev->base, "%s stall\n" , lcrtc->base.name); |
87 | return lsdc_crtc0_soft_reset(lcrtc); |
88 | } |
89 | |
90 | lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val: val | CFG_OUTPUT_ENABLE); |
91 | } |
92 | |
93 | static void lsdc_crtc0_disable(struct lsdc_crtc *lcrtc) |
94 | { |
95 | struct lsdc_device *ldev = lcrtc->ldev; |
96 | |
97 | lsdc_ureg32_clr(ldev, LSDC_CRTC0_CFG_REG, CFG_OUTPUT_ENABLE); |
98 | |
99 | udelay(9); |
100 | } |
101 | |
102 | static void lsdc_crtc1_enable(struct lsdc_crtc *lcrtc) |
103 | { |
104 | struct lsdc_device *ldev = lcrtc->ldev; |
105 | u32 val; |
106 | |
107 | /* |
108 | * This may happen in extremely rare cases, but a soft reset can |
109 | * bring it back to normal. We add a warning here, hoping to catch |
110 | * something if it happens. |
111 | */ |
112 | val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); |
113 | if (val & CRTC_ANCHORED) { |
114 | drm_warn(&ldev->base, "%s stall\n" , lcrtc->base.name); |
115 | return lsdc_crtc1_soft_reset(lcrtc); |
116 | } |
117 | |
118 | lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val: val | CFG_OUTPUT_ENABLE); |
119 | } |
120 | |
121 | static void lsdc_crtc1_disable(struct lsdc_crtc *lcrtc) |
122 | { |
123 | struct lsdc_device *ldev = lcrtc->ldev; |
124 | |
125 | lsdc_ureg32_clr(ldev, LSDC_CRTC1_CFG_REG, CFG_OUTPUT_ENABLE); |
126 | |
127 | udelay(9); |
128 | } |
129 | |
130 | /* All Loongson display controllers have hardware scanout position recoders */ |
131 | |
132 | static void lsdc_crtc0_scan_pos(struct lsdc_crtc *lcrtc, int *hpos, int *vpos) |
133 | { |
134 | struct lsdc_device *ldev = lcrtc->ldev; |
135 | u32 val; |
136 | |
137 | val = lsdc_rreg32(ldev, LSDC_CRTC0_SCAN_POS_REG); |
138 | |
139 | *hpos = val >> 16; |
140 | *vpos = val & 0xffff; |
141 | } |
142 | |
143 | static void lsdc_crtc1_scan_pos(struct lsdc_crtc *lcrtc, int *hpos, int *vpos) |
144 | { |
145 | struct lsdc_device *ldev = lcrtc->ldev; |
146 | u32 val; |
147 | |
148 | val = lsdc_rreg32(ldev, LSDC_CRTC1_SCAN_POS_REG); |
149 | |
150 | *hpos = val >> 16; |
151 | *vpos = val & 0xffff; |
152 | } |
153 | |
154 | static void lsdc_crtc0_enable_vblank(struct lsdc_crtc *lcrtc) |
155 | { |
156 | struct lsdc_device *ldev = lcrtc->ldev; |
157 | |
158 | lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN); |
159 | } |
160 | |
161 | static void lsdc_crtc0_disable_vblank(struct lsdc_crtc *lcrtc) |
162 | { |
163 | struct lsdc_device *ldev = lcrtc->ldev; |
164 | |
165 | lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC0_VSYNC_EN); |
166 | } |
167 | |
168 | static void lsdc_crtc1_enable_vblank(struct lsdc_crtc *lcrtc) |
169 | { |
170 | struct lsdc_device *ldev = lcrtc->ldev; |
171 | |
172 | lsdc_ureg32_set(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN); |
173 | } |
174 | |
175 | static void lsdc_crtc1_disable_vblank(struct lsdc_crtc *lcrtc) |
176 | { |
177 | struct lsdc_device *ldev = lcrtc->ldev; |
178 | |
179 | lsdc_ureg32_clr(ldev, LSDC_INT_REG, INT_CRTC1_VSYNC_EN); |
180 | } |
181 | |
182 | static void lsdc_crtc0_flip(struct lsdc_crtc *lcrtc) |
183 | { |
184 | struct lsdc_device *ldev = lcrtc->ldev; |
185 | |
186 | lsdc_ureg32_set(ldev, LSDC_CRTC0_CFG_REG, CFG_PAGE_FLIP); |
187 | } |
188 | |
189 | static void lsdc_crtc1_flip(struct lsdc_crtc *lcrtc) |
190 | { |
191 | struct lsdc_device *ldev = lcrtc->ldev; |
192 | |
193 | lsdc_ureg32_set(ldev, LSDC_CRTC1_CFG_REG, CFG_PAGE_FLIP); |
194 | } |
195 | |
196 | /* |
197 | * CRTC0 clone from CRTC1 or CRTC1 clone from CRTC0 using hardware logic |
198 | * This may be useful for custom cloning (TWIN) applications. Saving the |
199 | * bandwidth compared with the clone (mirroring) display mode provided by |
200 | * drm core. |
201 | */ |
202 | |
203 | static void lsdc_crtc0_clone(struct lsdc_crtc *lcrtc) |
204 | { |
205 | struct lsdc_device *ldev = lcrtc->ldev; |
206 | |
207 | lsdc_ureg32_set(ldev, LSDC_CRTC0_CFG_REG, CFG_HW_CLONE); |
208 | } |
209 | |
210 | static void lsdc_crtc1_clone(struct lsdc_crtc *lcrtc) |
211 | { |
212 | struct lsdc_device *ldev = lcrtc->ldev; |
213 | |
214 | lsdc_ureg32_set(ldev, LSDC_CRTC1_CFG_REG, CFG_HW_CLONE); |
215 | } |
216 | |
217 | static void lsdc_crtc0_set_mode(struct lsdc_crtc *lcrtc, |
218 | const struct drm_display_mode *mode) |
219 | { |
220 | struct lsdc_device *ldev = lcrtc->ldev; |
221 | |
222 | lsdc_wreg32(ldev, LSDC_CRTC0_HDISPLAY_REG, |
223 | val: (mode->crtc_htotal << 16) | mode->crtc_hdisplay); |
224 | |
225 | lsdc_wreg32(ldev, LSDC_CRTC0_VDISPLAY_REG, |
226 | val: (mode->crtc_vtotal << 16) | mode->crtc_vdisplay); |
227 | |
228 | lsdc_wreg32(ldev, LSDC_CRTC0_HSYNC_REG, |
229 | val: (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | HSYNC_EN); |
230 | |
231 | lsdc_wreg32(ldev, LSDC_CRTC0_VSYNC_REG, |
232 | val: (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | VSYNC_EN); |
233 | } |
234 | |
235 | static void lsdc_crtc1_set_mode(struct lsdc_crtc *lcrtc, |
236 | const struct drm_display_mode *mode) |
237 | { |
238 | struct lsdc_device *ldev = lcrtc->ldev; |
239 | |
240 | lsdc_wreg32(ldev, LSDC_CRTC1_HDISPLAY_REG, |
241 | val: (mode->crtc_htotal << 16) | mode->crtc_hdisplay); |
242 | |
243 | lsdc_wreg32(ldev, LSDC_CRTC1_VDISPLAY_REG, |
244 | val: (mode->crtc_vtotal << 16) | mode->crtc_vdisplay); |
245 | |
246 | lsdc_wreg32(ldev, LSDC_CRTC1_HSYNC_REG, |
247 | val: (mode->crtc_hsync_end << 16) | mode->crtc_hsync_start | HSYNC_EN); |
248 | |
249 | lsdc_wreg32(ldev, LSDC_CRTC1_VSYNC_REG, |
250 | val: (mode->crtc_vsync_end << 16) | mode->crtc_vsync_start | VSYNC_EN); |
251 | } |
252 | |
253 | /* |
254 | * This is required for S3 support. |
255 | * After resuming from suspend, LSDC_CRTCx_CFG_REG (x = 0 or 1) is filled |
256 | * with garbage value, which causes the CRTC hang there. |
257 | * |
258 | * This function provides minimal settings for the affected registers. |
259 | * This overrides the firmware's settings on startup, making the CRTC work |
260 | * on our own, similar to the functional of GPU POST (Power On Self Test). |
261 | * Only touch CRTC hardware-related parts. |
262 | */ |
263 | |
264 | static void lsdc_crtc0_reset(struct lsdc_crtc *lcrtc) |
265 | { |
266 | struct lsdc_device *ldev = lcrtc->ldev; |
267 | |
268 | lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, CFG_RESET_N | LSDC_PF_XRGB8888); |
269 | } |
270 | |
271 | static void lsdc_crtc1_reset(struct lsdc_crtc *lcrtc) |
272 | { |
273 | struct lsdc_device *ldev = lcrtc->ldev; |
274 | |
275 | lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, CFG_RESET_N | LSDC_PF_XRGB8888); |
276 | } |
277 | |
278 | static const struct lsdc_crtc_hw_ops ls7a1000_crtc_hw_ops[2] = { |
279 | { |
280 | .enable = lsdc_crtc0_enable, |
281 | .disable = lsdc_crtc0_disable, |
282 | .enable_vblank = lsdc_crtc0_enable_vblank, |
283 | .disable_vblank = lsdc_crtc0_disable_vblank, |
284 | .flip = lsdc_crtc0_flip, |
285 | .clone = lsdc_crtc0_clone, |
286 | .set_mode = lsdc_crtc0_set_mode, |
287 | .get_scan_pos = lsdc_crtc0_scan_pos, |
288 | .soft_reset = lsdc_crtc0_soft_reset, |
289 | .reset = lsdc_crtc0_reset, |
290 | }, |
291 | { |
292 | .enable = lsdc_crtc1_enable, |
293 | .disable = lsdc_crtc1_disable, |
294 | .enable_vblank = lsdc_crtc1_enable_vblank, |
295 | .disable_vblank = lsdc_crtc1_disable_vblank, |
296 | .flip = lsdc_crtc1_flip, |
297 | .clone = lsdc_crtc1_clone, |
298 | .set_mode = lsdc_crtc1_set_mode, |
299 | .get_scan_pos = lsdc_crtc1_scan_pos, |
300 | .soft_reset = lsdc_crtc1_soft_reset, |
301 | .reset = lsdc_crtc1_reset, |
302 | }, |
303 | }; |
304 | |
305 | /* |
306 | * The 32-bit hardware vblank counter has been available since LS7A2000 |
307 | * and LS2K2000. The counter increases even though the CRTC is disabled, |
308 | * it will be reset only if the CRTC is being soft reset. |
309 | * Those registers are also readable for ls7a1000, but its value does not |
310 | * change. |
311 | */ |
312 | |
313 | static u32 lsdc_crtc0_get_vblank_count(struct lsdc_crtc *lcrtc) |
314 | { |
315 | struct lsdc_device *ldev = lcrtc->ldev; |
316 | |
317 | return lsdc_rreg32(ldev, LSDC_CRTC0_VSYNC_COUNTER_REG); |
318 | } |
319 | |
320 | static u32 lsdc_crtc1_get_vblank_count(struct lsdc_crtc *lcrtc) |
321 | { |
322 | struct lsdc_device *ldev = lcrtc->ldev; |
323 | |
324 | return lsdc_rreg32(ldev, LSDC_CRTC1_VSYNC_COUNTER_REG); |
325 | } |
326 | |
327 | /* |
328 | * The DMA step bit fields are available since LS7A2000/LS2K2000, for |
329 | * supporting odd resolutions. But a large DMA step save the bandwidth. |
330 | * The larger, the better. Behavior of writing those bits on LS7A1000 |
331 | * or LS2K1000 is underfined. |
332 | */ |
333 | |
334 | static void lsdc_crtc0_set_dma_step(struct lsdc_crtc *lcrtc, |
335 | enum lsdc_dma_steps dma_step) |
336 | { |
337 | struct lsdc_device *ldev = lcrtc->ldev; |
338 | u32 val = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); |
339 | |
340 | val &= ~CFG_DMA_STEP_MASK; |
341 | val |= dma_step << CFG_DMA_STEP_SHIFT; |
342 | |
343 | lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val); |
344 | } |
345 | |
346 | static void lsdc_crtc1_set_dma_step(struct lsdc_crtc *lcrtc, |
347 | enum lsdc_dma_steps dma_step) |
348 | { |
349 | struct lsdc_device *ldev = lcrtc->ldev; |
350 | u32 val = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); |
351 | |
352 | val &= ~CFG_DMA_STEP_MASK; |
353 | val |= dma_step << CFG_DMA_STEP_SHIFT; |
354 | |
355 | lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val); |
356 | } |
357 | |
358 | static const struct lsdc_crtc_hw_ops ls7a2000_crtc_hw_ops[2] = { |
359 | { |
360 | .enable = lsdc_crtc0_enable, |
361 | .disable = lsdc_crtc0_disable, |
362 | .enable_vblank = lsdc_crtc0_enable_vblank, |
363 | .disable_vblank = lsdc_crtc0_disable_vblank, |
364 | .flip = lsdc_crtc0_flip, |
365 | .clone = lsdc_crtc0_clone, |
366 | .set_mode = lsdc_crtc0_set_mode, |
367 | .soft_reset = lsdc_crtc0_soft_reset, |
368 | .get_scan_pos = lsdc_crtc0_scan_pos, |
369 | .set_dma_step = lsdc_crtc0_set_dma_step, |
370 | .get_vblank_counter = lsdc_crtc0_get_vblank_count, |
371 | .reset = lsdc_crtc0_reset, |
372 | }, |
373 | { |
374 | .enable = lsdc_crtc1_enable, |
375 | .disable = lsdc_crtc1_disable, |
376 | .enable_vblank = lsdc_crtc1_enable_vblank, |
377 | .disable_vblank = lsdc_crtc1_disable_vblank, |
378 | .flip = lsdc_crtc1_flip, |
379 | .clone = lsdc_crtc1_clone, |
380 | .set_mode = lsdc_crtc1_set_mode, |
381 | .get_scan_pos = lsdc_crtc1_scan_pos, |
382 | .soft_reset = lsdc_crtc1_soft_reset, |
383 | .set_dma_step = lsdc_crtc1_set_dma_step, |
384 | .get_vblank_counter = lsdc_crtc1_get_vblank_count, |
385 | .reset = lsdc_crtc1_reset, |
386 | }, |
387 | }; |
388 | |
389 | static void lsdc_crtc_reset(struct drm_crtc *crtc) |
390 | { |
391 | struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); |
392 | const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; |
393 | struct lsdc_crtc_state *priv_crtc_state; |
394 | |
395 | if (crtc->state) |
396 | crtc->funcs->atomic_destroy_state(crtc, crtc->state); |
397 | |
398 | priv_crtc_state = kzalloc(size: sizeof(*priv_crtc_state), GFP_KERNEL); |
399 | |
400 | if (!priv_crtc_state) |
401 | __drm_atomic_helper_crtc_reset(crtc, NULL); |
402 | else |
403 | __drm_atomic_helper_crtc_reset(crtc, state: &priv_crtc_state->base); |
404 | |
405 | /* Reset the CRTC hardware, this is required for S3 support */ |
406 | ops->reset(lcrtc); |
407 | } |
408 | |
409 | static void lsdc_crtc_atomic_destroy_state(struct drm_crtc *crtc, |
410 | struct drm_crtc_state *state) |
411 | { |
412 | struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(base: state); |
413 | |
414 | __drm_atomic_helper_crtc_destroy_state(state: &priv_state->base); |
415 | |
416 | kfree(objp: priv_state); |
417 | } |
418 | |
419 | static struct drm_crtc_state * |
420 | lsdc_crtc_atomic_duplicate_state(struct drm_crtc *crtc) |
421 | { |
422 | struct lsdc_crtc_state *new_priv_state; |
423 | struct lsdc_crtc_state *old_priv_state; |
424 | |
425 | new_priv_state = kzalloc(size: sizeof(*new_priv_state), GFP_KERNEL); |
426 | if (!new_priv_state) |
427 | return NULL; |
428 | |
429 | __drm_atomic_helper_crtc_duplicate_state(crtc, state: &new_priv_state->base); |
430 | |
431 | old_priv_state = to_lsdc_crtc_state(base: crtc->state); |
432 | |
433 | memcpy(&new_priv_state->pparms, &old_priv_state->pparms, |
434 | sizeof(new_priv_state->pparms)); |
435 | |
436 | return &new_priv_state->base; |
437 | } |
438 | |
439 | static u32 lsdc_crtc_get_vblank_counter(struct drm_crtc *crtc) |
440 | { |
441 | struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); |
442 | |
443 | /* 32-bit hardware vblank counter */ |
444 | return lcrtc->hw_ops->get_vblank_counter(lcrtc); |
445 | } |
446 | |
447 | static int lsdc_crtc_enable_vblank(struct drm_crtc *crtc) |
448 | { |
449 | struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); |
450 | |
451 | if (!lcrtc->has_vblank) |
452 | return -EINVAL; |
453 | |
454 | lcrtc->hw_ops->enable_vblank(lcrtc); |
455 | |
456 | return 0; |
457 | } |
458 | |
459 | static void lsdc_crtc_disable_vblank(struct drm_crtc *crtc) |
460 | { |
461 | struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); |
462 | |
463 | if (!lcrtc->has_vblank) |
464 | return; |
465 | |
466 | lcrtc->hw_ops->disable_vblank(lcrtc); |
467 | } |
468 | |
469 | /* |
470 | * CRTC related debugfs |
471 | * Primary planes and cursor planes belong to the CRTC as well. |
472 | * For the sake of convenience, plane-related registers are also add here. |
473 | */ |
474 | |
475 | #define REG_DEF(reg) { \ |
476 | .name = __stringify_1(LSDC_##reg##_REG), \ |
477 | .offset = LSDC_##reg##_REG, \ |
478 | } |
479 | |
480 | static const struct lsdc_reg32 lsdc_crtc_regs_array[2][21] = { |
481 | [0] = { |
482 | REG_DEF(CRTC0_CFG), |
483 | REG_DEF(CRTC0_FB_ORIGIN), |
484 | REG_DEF(CRTC0_DVO_CONF), |
485 | REG_DEF(CRTC0_HDISPLAY), |
486 | REG_DEF(CRTC0_HSYNC), |
487 | REG_DEF(CRTC0_VDISPLAY), |
488 | REG_DEF(CRTC0_VSYNC), |
489 | REG_DEF(CRTC0_GAMMA_INDEX), |
490 | REG_DEF(CRTC0_GAMMA_DATA), |
491 | REG_DEF(CRTC0_SYNC_DEVIATION), |
492 | REG_DEF(CRTC0_VSYNC_COUNTER), |
493 | REG_DEF(CRTC0_SCAN_POS), |
494 | REG_DEF(CRTC0_STRIDE), |
495 | REG_DEF(CRTC0_FB1_ADDR_HI), |
496 | REG_DEF(CRTC0_FB1_ADDR_LO), |
497 | REG_DEF(CRTC0_FB0_ADDR_HI), |
498 | REG_DEF(CRTC0_FB0_ADDR_LO), |
499 | REG_DEF(CURSOR0_CFG), |
500 | REG_DEF(CURSOR0_POSITION), |
501 | REG_DEF(CURSOR0_BG_COLOR), |
502 | REG_DEF(CURSOR0_FG_COLOR), |
503 | }, |
504 | [1] = { |
505 | REG_DEF(CRTC1_CFG), |
506 | REG_DEF(CRTC1_FB_ORIGIN), |
507 | REG_DEF(CRTC1_DVO_CONF), |
508 | REG_DEF(CRTC1_HDISPLAY), |
509 | REG_DEF(CRTC1_HSYNC), |
510 | REG_DEF(CRTC1_VDISPLAY), |
511 | REG_DEF(CRTC1_VSYNC), |
512 | REG_DEF(CRTC1_GAMMA_INDEX), |
513 | REG_DEF(CRTC1_GAMMA_DATA), |
514 | REG_DEF(CRTC1_SYNC_DEVIATION), |
515 | REG_DEF(CRTC1_VSYNC_COUNTER), |
516 | REG_DEF(CRTC1_SCAN_POS), |
517 | REG_DEF(CRTC1_STRIDE), |
518 | REG_DEF(CRTC1_FB1_ADDR_HI), |
519 | REG_DEF(CRTC1_FB1_ADDR_LO), |
520 | REG_DEF(CRTC1_FB0_ADDR_HI), |
521 | REG_DEF(CRTC1_FB0_ADDR_LO), |
522 | REG_DEF(CURSOR1_CFG), |
523 | REG_DEF(CURSOR1_POSITION), |
524 | REG_DEF(CURSOR1_BG_COLOR), |
525 | REG_DEF(CURSOR1_FG_COLOR), |
526 | }, |
527 | }; |
528 | |
529 | static int lsdc_crtc_show_regs(struct seq_file *m, void *arg) |
530 | { |
531 | struct drm_info_node *node = (struct drm_info_node *)m->private; |
532 | struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data; |
533 | struct lsdc_device *ldev = lcrtc->ldev; |
534 | unsigned int i; |
535 | |
536 | for (i = 0; i < lcrtc->nreg; i++) { |
537 | const struct lsdc_reg32 *preg = &lcrtc->preg[i]; |
538 | u32 offset = preg->offset; |
539 | |
540 | seq_printf(m, fmt: "%s (0x%04x): 0x%08x\n" , |
541 | preg->name, offset, lsdc_rreg32(ldev, offset)); |
542 | } |
543 | |
544 | return 0; |
545 | } |
546 | |
547 | static int lsdc_crtc_show_scan_position(struct seq_file *m, void *arg) |
548 | { |
549 | struct drm_info_node *node = (struct drm_info_node *)m->private; |
550 | struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data; |
551 | int x, y; |
552 | |
553 | lcrtc->hw_ops->get_scan_pos(lcrtc, &x, &y); |
554 | seq_printf(m, fmt: "Scanout position: x: %08u, y: %08u\n" , x, y); |
555 | |
556 | return 0; |
557 | } |
558 | |
559 | static int lsdc_crtc_show_vblank_counter(struct seq_file *m, void *arg) |
560 | { |
561 | struct drm_info_node *node = (struct drm_info_node *)m->private; |
562 | struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data; |
563 | |
564 | if (lcrtc->hw_ops->get_vblank_counter) |
565 | seq_printf(m, fmt: "%s vblank counter: %08u\n\n" , lcrtc->base.name, |
566 | lcrtc->hw_ops->get_vblank_counter(lcrtc)); |
567 | |
568 | return 0; |
569 | } |
570 | |
571 | static int lsdc_pixpll_show_clock(struct seq_file *m, void *arg) |
572 | { |
573 | struct drm_info_node *node = (struct drm_info_node *)m->private; |
574 | struct lsdc_crtc *lcrtc = (struct lsdc_crtc *)node->info_ent->data; |
575 | struct lsdc_pixpll *pixpll = &lcrtc->pixpll; |
576 | const struct lsdc_pixpll_funcs *funcs = pixpll->funcs; |
577 | struct drm_crtc *crtc = &lcrtc->base; |
578 | struct drm_display_mode *mode = &crtc->state->mode; |
579 | struct drm_printer printer = drm_seq_file_printer(f: m); |
580 | unsigned int out_khz; |
581 | |
582 | out_khz = funcs->get_rate(pixpll); |
583 | |
584 | seq_printf(m, fmt: "%s: %dx%d@%d\n" , crtc->name, |
585 | mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode)); |
586 | |
587 | seq_printf(m, fmt: "Pixel clock required: %d kHz\n" , mode->clock); |
588 | seq_printf(m, fmt: "Actual frequency output: %u kHz\n" , out_khz); |
589 | seq_printf(m, fmt: "Diff: %d kHz\n" , out_khz - mode->clock); |
590 | |
591 | funcs->print(pixpll, &printer); |
592 | |
593 | return 0; |
594 | } |
595 | |
596 | static struct drm_info_list lsdc_crtc_debugfs_list[2][4] = { |
597 | [0] = { |
598 | { "regs" , lsdc_crtc_show_regs, 0, NULL }, |
599 | { "pixclk" , lsdc_pixpll_show_clock, 0, NULL }, |
600 | { "scanpos" , lsdc_crtc_show_scan_position, 0, NULL }, |
601 | { "vblanks" , lsdc_crtc_show_vblank_counter, 0, NULL }, |
602 | }, |
603 | [1] = { |
604 | { "regs" , lsdc_crtc_show_regs, 0, NULL }, |
605 | { "pixclk" , lsdc_pixpll_show_clock, 0, NULL }, |
606 | { "scanpos" , lsdc_crtc_show_scan_position, 0, NULL }, |
607 | { "vblanks" , lsdc_crtc_show_vblank_counter, 0, NULL }, |
608 | }, |
609 | }; |
610 | |
611 | /* operate manually */ |
612 | |
613 | static int lsdc_crtc_man_op_show(struct seq_file *m, void *data) |
614 | { |
615 | seq_puts(m, s: "soft_reset: soft reset this CRTC\n" ); |
616 | seq_puts(m, s: "enable: enable this CRTC\n" ); |
617 | seq_puts(m, s: "disable: disable this CRTC\n" ); |
618 | seq_puts(m, s: "flip: trigger the page flip\n" ); |
619 | seq_puts(m, s: "clone: clone the another crtc with hardware logic\n" ); |
620 | |
621 | return 0; |
622 | } |
623 | |
624 | static int lsdc_crtc_man_op_open(struct inode *inode, struct file *file) |
625 | { |
626 | struct drm_crtc *crtc = inode->i_private; |
627 | |
628 | return single_open(file, lsdc_crtc_man_op_show, crtc); |
629 | } |
630 | |
631 | static ssize_t lsdc_crtc_man_op_write(struct file *file, |
632 | const char __user *ubuf, |
633 | size_t len, |
634 | loff_t *offp) |
635 | { |
636 | struct seq_file *m = file->private_data; |
637 | struct lsdc_crtc *lcrtc = m->private; |
638 | const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; |
639 | char buf[16]; |
640 | |
641 | if (len > sizeof(buf) - 1) |
642 | return -EINVAL; |
643 | |
644 | if (copy_from_user(to: buf, from: ubuf, n: len)) |
645 | return -EFAULT; |
646 | |
647 | buf[len] = '\0'; |
648 | |
649 | if (sysfs_streq(s1: buf, s2: "soft_reset" )) |
650 | ops->soft_reset(lcrtc); |
651 | else if (sysfs_streq(s1: buf, s2: "enable" )) |
652 | ops->enable(lcrtc); |
653 | else if (sysfs_streq(s1: buf, s2: "disable" )) |
654 | ops->disable(lcrtc); |
655 | else if (sysfs_streq(s1: buf, s2: "flip" )) |
656 | ops->flip(lcrtc); |
657 | else if (sysfs_streq(s1: buf, s2: "clone" )) |
658 | ops->clone(lcrtc); |
659 | |
660 | return len; |
661 | } |
662 | |
663 | static const struct file_operations lsdc_crtc_man_op_fops = { |
664 | .owner = THIS_MODULE, |
665 | .open = lsdc_crtc_man_op_open, |
666 | .read = seq_read, |
667 | .llseek = seq_lseek, |
668 | .release = single_release, |
669 | .write = lsdc_crtc_man_op_write, |
670 | }; |
671 | |
672 | static int lsdc_crtc_late_register(struct drm_crtc *crtc) |
673 | { |
674 | struct lsdc_display_pipe *dispipe = crtc_to_display_pipe(crtc); |
675 | struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); |
676 | struct drm_minor *minor = crtc->dev->primary; |
677 | unsigned int index = dispipe->index; |
678 | unsigned int i; |
679 | |
680 | lcrtc->preg = lsdc_crtc_regs_array[index]; |
681 | lcrtc->nreg = ARRAY_SIZE(lsdc_crtc_regs_array[index]); |
682 | lcrtc->p_info_list = lsdc_crtc_debugfs_list[index]; |
683 | lcrtc->n_info_list = ARRAY_SIZE(lsdc_crtc_debugfs_list[index]); |
684 | |
685 | for (i = 0; i < lcrtc->n_info_list; ++i) |
686 | lcrtc->p_info_list[i].data = lcrtc; |
687 | |
688 | drm_debugfs_create_files(files: lcrtc->p_info_list, count: lcrtc->n_info_list, |
689 | root: crtc->debugfs_entry, minor); |
690 | |
691 | /* Manual operations supported */ |
692 | debugfs_create_file(name: "ops" , mode: 0644, parent: crtc->debugfs_entry, data: lcrtc, |
693 | fops: &lsdc_crtc_man_op_fops); |
694 | |
695 | return 0; |
696 | } |
697 | |
698 | static void lsdc_crtc_atomic_print_state(struct drm_printer *p, |
699 | const struct drm_crtc_state *state) |
700 | { |
701 | const struct lsdc_crtc_state *priv_state; |
702 | const struct lsdc_pixpll_parms *pparms; |
703 | |
704 | priv_state = container_of_const(state, struct lsdc_crtc_state, base); |
705 | pparms = &priv_state->pparms; |
706 | |
707 | drm_printf(p, f: "\tInput clock divider = %u\n" , pparms->div_ref); |
708 | drm_printf(p, f: "\tMedium clock multiplier = %u\n" , pparms->loopc); |
709 | drm_printf(p, f: "\tOutput clock divider = %u\n" , pparms->div_out); |
710 | } |
711 | |
712 | static const struct drm_crtc_funcs ls7a1000_crtc_funcs = { |
713 | .reset = lsdc_crtc_reset, |
714 | .destroy = drm_crtc_cleanup, |
715 | .set_config = drm_atomic_helper_set_config, |
716 | .page_flip = drm_atomic_helper_page_flip, |
717 | .atomic_duplicate_state = lsdc_crtc_atomic_duplicate_state, |
718 | .atomic_destroy_state = lsdc_crtc_atomic_destroy_state, |
719 | .late_register = lsdc_crtc_late_register, |
720 | .enable_vblank = lsdc_crtc_enable_vblank, |
721 | .disable_vblank = lsdc_crtc_disable_vblank, |
722 | .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, |
723 | .atomic_print_state = lsdc_crtc_atomic_print_state, |
724 | }; |
725 | |
726 | static const struct drm_crtc_funcs ls7a2000_crtc_funcs = { |
727 | .reset = lsdc_crtc_reset, |
728 | .destroy = drm_crtc_cleanup, |
729 | .set_config = drm_atomic_helper_set_config, |
730 | .page_flip = drm_atomic_helper_page_flip, |
731 | .atomic_duplicate_state = lsdc_crtc_atomic_duplicate_state, |
732 | .atomic_destroy_state = lsdc_crtc_atomic_destroy_state, |
733 | .late_register = lsdc_crtc_late_register, |
734 | .get_vblank_counter = lsdc_crtc_get_vblank_counter, |
735 | .enable_vblank = lsdc_crtc_enable_vblank, |
736 | .disable_vblank = lsdc_crtc_disable_vblank, |
737 | .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, |
738 | .atomic_print_state = lsdc_crtc_atomic_print_state, |
739 | }; |
740 | |
741 | static enum drm_mode_status |
742 | lsdc_crtc_mode_valid(struct drm_crtc *crtc, const struct drm_display_mode *mode) |
743 | { |
744 | struct drm_device *ddev = crtc->dev; |
745 | struct lsdc_device *ldev = to_lsdc(ddev); |
746 | const struct lsdc_desc *descp = ldev->descp; |
747 | unsigned int pitch; |
748 | |
749 | if (mode->hdisplay > descp->max_width) |
750 | return MODE_BAD_HVALUE; |
751 | |
752 | if (mode->vdisplay > descp->max_height) |
753 | return MODE_BAD_VVALUE; |
754 | |
755 | if (mode->clock > descp->max_pixel_clk) { |
756 | drm_dbg_kms(ddev, "mode %dx%d, pixel clock=%d is too high\n" , |
757 | mode->hdisplay, mode->vdisplay, mode->clock); |
758 | return MODE_CLOCK_HIGH; |
759 | } |
760 | |
761 | /* 4 for DRM_FORMAT_XRGB8888 */ |
762 | pitch = mode->hdisplay * 4; |
763 | |
764 | if (pitch % descp->pitch_align) { |
765 | drm_dbg_kms(ddev, "align to %u bytes is required: %u\n" , |
766 | descp->pitch_align, pitch); |
767 | return MODE_BAD_WIDTH; |
768 | } |
769 | |
770 | return MODE_OK; |
771 | } |
772 | |
773 | static int lsdc_pixpll_atomic_check(struct drm_crtc *crtc, |
774 | struct drm_crtc_state *state) |
775 | { |
776 | struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); |
777 | struct lsdc_pixpll *pixpll = &lcrtc->pixpll; |
778 | const struct lsdc_pixpll_funcs *pfuncs = pixpll->funcs; |
779 | struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(base: state); |
780 | unsigned int clock = state->mode.clock; |
781 | int ret; |
782 | |
783 | ret = pfuncs->compute(pixpll, clock, &priv_state->pparms); |
784 | if (ret) { |
785 | drm_warn(crtc->dev, "Failed to find PLL params for %ukHz\n" , |
786 | clock); |
787 | return -EINVAL; |
788 | } |
789 | |
790 | return 0; |
791 | } |
792 | |
793 | static int lsdc_crtc_helper_atomic_check(struct drm_crtc *crtc, |
794 | struct drm_atomic_state *state) |
795 | { |
796 | struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); |
797 | |
798 | if (!crtc_state->enable) |
799 | return 0; |
800 | |
801 | return lsdc_pixpll_atomic_check(crtc, state: crtc_state); |
802 | } |
803 | |
804 | static void lsdc_crtc_mode_set_nofb(struct drm_crtc *crtc) |
805 | { |
806 | struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); |
807 | const struct lsdc_crtc_hw_ops *crtc_hw_ops = lcrtc->hw_ops; |
808 | struct lsdc_pixpll *pixpll = &lcrtc->pixpll; |
809 | const struct lsdc_pixpll_funcs *pixpll_funcs = pixpll->funcs; |
810 | struct drm_crtc_state *state = crtc->state; |
811 | struct drm_display_mode *mode = &state->mode; |
812 | struct lsdc_crtc_state *priv_state = to_lsdc_crtc_state(base: state); |
813 | |
814 | pixpll_funcs->update(pixpll, &priv_state->pparms); |
815 | |
816 | if (crtc_hw_ops->set_dma_step) { |
817 | unsigned int width_in_bytes = mode->hdisplay * 4; |
818 | enum lsdc_dma_steps dma_step; |
819 | |
820 | /* |
821 | * Using DMA step as large as possible, for improving |
822 | * hardware DMA efficiency. |
823 | */ |
824 | if (width_in_bytes % 256 == 0) |
825 | dma_step = LSDC_DMA_STEP_256_BYTES; |
826 | else if (width_in_bytes % 128 == 0) |
827 | dma_step = LSDC_DMA_STEP_128_BYTES; |
828 | else if (width_in_bytes % 64 == 0) |
829 | dma_step = LSDC_DMA_STEP_64_BYTES; |
830 | else /* width_in_bytes % 32 == 0 */ |
831 | dma_step = LSDC_DMA_STEP_32_BYTES; |
832 | |
833 | crtc_hw_ops->set_dma_step(lcrtc, dma_step); |
834 | } |
835 | |
836 | crtc_hw_ops->set_mode(lcrtc, mode); |
837 | } |
838 | |
839 | static void lsdc_crtc_send_vblank(struct drm_crtc *crtc) |
840 | { |
841 | struct drm_device *ddev = crtc->dev; |
842 | unsigned long flags; |
843 | |
844 | if (!crtc->state || !crtc->state->event) |
845 | return; |
846 | |
847 | drm_dbg(ddev, "Send vblank manually\n" ); |
848 | |
849 | spin_lock_irqsave(&ddev->event_lock, flags); |
850 | drm_crtc_send_vblank_event(crtc, e: crtc->state->event); |
851 | crtc->state->event = NULL; |
852 | spin_unlock_irqrestore(lock: &ddev->event_lock, flags); |
853 | } |
854 | |
855 | static void lsdc_crtc_atomic_enable(struct drm_crtc *crtc, |
856 | struct drm_atomic_state *state) |
857 | { |
858 | struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); |
859 | |
860 | if (lcrtc->has_vblank) |
861 | drm_crtc_vblank_on(crtc); |
862 | |
863 | lcrtc->hw_ops->enable(lcrtc); |
864 | } |
865 | |
866 | static void lsdc_crtc_atomic_disable(struct drm_crtc *crtc, |
867 | struct drm_atomic_state *state) |
868 | { |
869 | struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); |
870 | |
871 | if (lcrtc->has_vblank) |
872 | drm_crtc_vblank_off(crtc); |
873 | |
874 | lcrtc->hw_ops->disable(lcrtc); |
875 | |
876 | /* |
877 | * Make sure we issue a vblank event after disabling the CRTC if |
878 | * someone was waiting it. |
879 | */ |
880 | lsdc_crtc_send_vblank(crtc); |
881 | } |
882 | |
883 | static void lsdc_crtc_atomic_flush(struct drm_crtc *crtc, |
884 | struct drm_atomic_state *state) |
885 | { |
886 | spin_lock_irq(lock: &crtc->dev->event_lock); |
887 | if (crtc->state->event) { |
888 | if (drm_crtc_vblank_get(crtc) == 0) |
889 | drm_crtc_arm_vblank_event(crtc, e: crtc->state->event); |
890 | else |
891 | drm_crtc_send_vblank_event(crtc, e: crtc->state->event); |
892 | crtc->state->event = NULL; |
893 | } |
894 | spin_unlock_irq(lock: &crtc->dev->event_lock); |
895 | } |
896 | |
897 | static bool lsdc_crtc_get_scanout_position(struct drm_crtc *crtc, |
898 | bool in_vblank_irq, |
899 | int *vpos, |
900 | int *hpos, |
901 | ktime_t *stime, |
902 | ktime_t *etime, |
903 | const struct drm_display_mode *mode) |
904 | { |
905 | struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); |
906 | const struct lsdc_crtc_hw_ops *ops = lcrtc->hw_ops; |
907 | int vsw, vbp, vactive_start, vactive_end, vfp_end; |
908 | int x, y; |
909 | |
910 | vsw = mode->crtc_vsync_end - mode->crtc_vsync_start; |
911 | vbp = mode->crtc_vtotal - mode->crtc_vsync_end; |
912 | |
913 | vactive_start = vsw + vbp + 1; |
914 | vactive_end = vactive_start + mode->crtc_vdisplay; |
915 | |
916 | /* last scan line before VSYNC */ |
917 | vfp_end = mode->crtc_vtotal; |
918 | |
919 | if (stime) |
920 | *stime = ktime_get(); |
921 | |
922 | ops->get_scan_pos(lcrtc, &x, &y); |
923 | |
924 | if (y > vactive_end) |
925 | y = y - vfp_end - vactive_start; |
926 | else |
927 | y -= vactive_start; |
928 | |
929 | *vpos = y; |
930 | *hpos = 0; |
931 | |
932 | if (etime) |
933 | *etime = ktime_get(); |
934 | |
935 | return true; |
936 | } |
937 | |
938 | static const struct drm_crtc_helper_funcs lsdc_crtc_helper_funcs = { |
939 | .mode_valid = lsdc_crtc_mode_valid, |
940 | .mode_set_nofb = lsdc_crtc_mode_set_nofb, |
941 | .atomic_enable = lsdc_crtc_atomic_enable, |
942 | .atomic_disable = lsdc_crtc_atomic_disable, |
943 | .atomic_check = lsdc_crtc_helper_atomic_check, |
944 | .atomic_flush = lsdc_crtc_atomic_flush, |
945 | .get_scanout_position = lsdc_crtc_get_scanout_position, |
946 | }; |
947 | |
948 | int ls7a1000_crtc_init(struct drm_device *ddev, |
949 | struct drm_crtc *crtc, |
950 | struct drm_plane *primary, |
951 | struct drm_plane *cursor, |
952 | unsigned int index, |
953 | bool has_vblank) |
954 | { |
955 | struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); |
956 | int ret; |
957 | |
958 | ret = lsdc_pixpll_init(this: &lcrtc->pixpll, ddev, index); |
959 | if (ret) { |
960 | drm_err(ddev, "pixel pll init failed: %d\n" , ret); |
961 | return ret; |
962 | } |
963 | |
964 | lcrtc->ldev = to_lsdc(ddev); |
965 | lcrtc->has_vblank = has_vblank; |
966 | lcrtc->hw_ops = &ls7a1000_crtc_hw_ops[index]; |
967 | |
968 | ret = drm_crtc_init_with_planes(dev: ddev, crtc, primary, cursor, |
969 | funcs: &ls7a1000_crtc_funcs, |
970 | name: "LS-CRTC-%d" , index); |
971 | if (ret) { |
972 | drm_err(ddev, "crtc init with planes failed: %d\n" , ret); |
973 | return ret; |
974 | } |
975 | |
976 | drm_crtc_helper_add(crtc, funcs: &lsdc_crtc_helper_funcs); |
977 | |
978 | ret = drm_mode_crtc_set_gamma_size(crtc, gamma_size: 256); |
979 | if (ret) |
980 | return ret; |
981 | |
982 | drm_crtc_enable_color_mgmt(crtc, degamma_lut_size: 0, has_ctm: false, gamma_lut_size: 256); |
983 | |
984 | return 0; |
985 | } |
986 | |
987 | int ls7a2000_crtc_init(struct drm_device *ddev, |
988 | struct drm_crtc *crtc, |
989 | struct drm_plane *primary, |
990 | struct drm_plane *cursor, |
991 | unsigned int index, |
992 | bool has_vblank) |
993 | { |
994 | struct lsdc_crtc *lcrtc = to_lsdc_crtc(crtc); |
995 | int ret; |
996 | |
997 | ret = lsdc_pixpll_init(this: &lcrtc->pixpll, ddev, index); |
998 | if (ret) { |
999 | drm_err(ddev, "crtc init with pll failed: %d\n" , ret); |
1000 | return ret; |
1001 | } |
1002 | |
1003 | lcrtc->ldev = to_lsdc(ddev); |
1004 | lcrtc->has_vblank = has_vblank; |
1005 | lcrtc->hw_ops = &ls7a2000_crtc_hw_ops[index]; |
1006 | |
1007 | ret = drm_crtc_init_with_planes(dev: ddev, crtc, primary, cursor, |
1008 | funcs: &ls7a2000_crtc_funcs, |
1009 | name: "LS-CRTC-%u" , index); |
1010 | if (ret) { |
1011 | drm_err(ddev, "crtc init with planes failed: %d\n" , ret); |
1012 | return ret; |
1013 | } |
1014 | |
1015 | drm_crtc_helper_add(crtc, funcs: &lsdc_crtc_helper_funcs); |
1016 | |
1017 | ret = drm_mode_crtc_set_gamma_size(crtc, gamma_size: 256); |
1018 | if (ret) |
1019 | return ret; |
1020 | |
1021 | drm_crtc_enable_color_mgmt(crtc, degamma_lut_size: 0, has_ctm: false, gamma_lut_size: 256); |
1022 | |
1023 | return 0; |
1024 | } |
1025 | |