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_framebuffer.h> |
11 | #include <drm/drm_gem_atomic_helper.h> |
12 | |
13 | #include "lsdc_drv.h" |
14 | #include "lsdc_regs.h" |
15 | #include "lsdc_ttm.h" |
16 | |
17 | static const u32 lsdc_primary_formats[] = { |
18 | DRM_FORMAT_XRGB8888, |
19 | }; |
20 | |
21 | static const u32 lsdc_cursor_formats[] = { |
22 | DRM_FORMAT_ARGB8888, |
23 | }; |
24 | |
25 | static const u64 lsdc_fb_format_modifiers[] = { |
26 | DRM_FORMAT_MOD_LINEAR, |
27 | DRM_FORMAT_MOD_INVALID |
28 | }; |
29 | |
30 | static unsigned int lsdc_get_fb_offset(struct drm_framebuffer *fb, |
31 | struct drm_plane_state *state) |
32 | { |
33 | unsigned int offset = fb->offsets[0]; |
34 | |
35 | offset += fb->format->cpp[0] * (state->src_x >> 16); |
36 | offset += fb->pitches[0] * (state->src_y >> 16); |
37 | |
38 | return offset; |
39 | } |
40 | |
41 | static u64 lsdc_fb_base_addr(struct drm_framebuffer *fb) |
42 | { |
43 | struct lsdc_device *ldev = to_lsdc(ddev: fb->dev); |
44 | struct lsdc_bo *lbo = gem_to_lsdc_bo(gem: fb->obj[0]); |
45 | |
46 | return lsdc_bo_gpu_offset(lbo) + ldev->vram_base; |
47 | } |
48 | |
49 | static int lsdc_primary_atomic_check(struct drm_plane *plane, |
50 | struct drm_atomic_state *state) |
51 | { |
52 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); |
53 | struct drm_crtc *crtc = new_plane_state->crtc; |
54 | struct drm_crtc_state *new_crtc_state; |
55 | |
56 | if (!crtc) |
57 | return 0; |
58 | |
59 | new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); |
60 | |
61 | return drm_atomic_helper_check_plane_state(plane_state: new_plane_state, |
62 | crtc_state: new_crtc_state, |
63 | DRM_PLANE_NO_SCALING, |
64 | DRM_PLANE_NO_SCALING, |
65 | can_position: false, can_update_disabled: true); |
66 | } |
67 | |
68 | static void lsdc_primary_atomic_update(struct drm_plane *plane, |
69 | struct drm_atomic_state *state) |
70 | { |
71 | struct lsdc_primary *primary = to_lsdc_primary(plane); |
72 | const struct lsdc_primary_plane_ops *ops = primary->ops; |
73 | struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); |
74 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); |
75 | struct drm_framebuffer *new_fb = new_plane_state->fb; |
76 | struct drm_framebuffer *old_fb = old_plane_state->fb; |
77 | u64 fb_addr = lsdc_fb_base_addr(fb: new_fb); |
78 | |
79 | fb_addr += lsdc_get_fb_offset(fb: new_fb, state: new_plane_state); |
80 | |
81 | ops->update_fb_addr(primary, fb_addr); |
82 | ops->update_fb_stride(primary, new_fb->pitches[0]); |
83 | |
84 | if (!old_fb || old_fb->format != new_fb->format) |
85 | ops->update_fb_format(primary, new_fb->format); |
86 | } |
87 | |
88 | static void lsdc_primary_atomic_disable(struct drm_plane *plane, |
89 | struct drm_atomic_state *state) |
90 | { |
91 | /* |
92 | * Do nothing, just prevent call into atomic_update(). |
93 | * Writing the format as LSDC_PF_NONE can disable the primary, |
94 | * But it seems not necessary... |
95 | */ |
96 | drm_dbg(plane->dev, "%s disabled\n" , plane->name); |
97 | } |
98 | |
99 | static int lsdc_plane_prepare_fb(struct drm_plane *plane, |
100 | struct drm_plane_state *new_state) |
101 | { |
102 | struct drm_framebuffer *fb = new_state->fb; |
103 | struct lsdc_bo *lbo; |
104 | u64 gpu_vaddr; |
105 | int ret; |
106 | |
107 | if (!fb) |
108 | return 0; |
109 | |
110 | lbo = gem_to_lsdc_bo(gem: fb->obj[0]); |
111 | |
112 | ret = lsdc_bo_reserve(lbo); |
113 | if (unlikely(ret)) { |
114 | drm_err(plane->dev, "bo %p reserve failed\n" , lbo); |
115 | return ret; |
116 | } |
117 | |
118 | ret = lsdc_bo_pin(lbo, LSDC_GEM_DOMAIN_VRAM, gpu_addr: &gpu_vaddr); |
119 | |
120 | lsdc_bo_unreserve(lbo); |
121 | |
122 | if (unlikely(ret)) { |
123 | drm_err(plane->dev, "bo %p pin failed\n" , lbo); |
124 | return ret; |
125 | } |
126 | |
127 | lsdc_bo_ref(lbo); |
128 | |
129 | if (plane->type != DRM_PLANE_TYPE_CURSOR) |
130 | drm_dbg(plane->dev, |
131 | "%s[%p] pin at 0x%llx, bo size: %zu\n" , |
132 | plane->name, lbo, gpu_vaddr, lsdc_bo_size(lbo)); |
133 | |
134 | return drm_gem_plane_helper_prepare_fb(plane, state: new_state); |
135 | } |
136 | |
137 | static void lsdc_plane_cleanup_fb(struct drm_plane *plane, |
138 | struct drm_plane_state *old_state) |
139 | { |
140 | struct drm_framebuffer *fb = old_state->fb; |
141 | struct lsdc_bo *lbo; |
142 | int ret; |
143 | |
144 | if (!fb) |
145 | return; |
146 | |
147 | lbo = gem_to_lsdc_bo(gem: fb->obj[0]); |
148 | |
149 | ret = lsdc_bo_reserve(lbo); |
150 | if (unlikely(ret)) { |
151 | drm_err(plane->dev, "%p reserve failed\n" , lbo); |
152 | return; |
153 | } |
154 | |
155 | lsdc_bo_unpin(lbo); |
156 | |
157 | lsdc_bo_unreserve(lbo); |
158 | |
159 | lsdc_bo_unref(lbo); |
160 | |
161 | if (plane->type != DRM_PLANE_TYPE_CURSOR) |
162 | drm_dbg(plane->dev, "%s unpin\n" , plane->name); |
163 | } |
164 | |
165 | static const struct drm_plane_helper_funcs lsdc_primary_helper_funcs = { |
166 | .prepare_fb = lsdc_plane_prepare_fb, |
167 | .cleanup_fb = lsdc_plane_cleanup_fb, |
168 | .atomic_check = lsdc_primary_atomic_check, |
169 | .atomic_update = lsdc_primary_atomic_update, |
170 | .atomic_disable = lsdc_primary_atomic_disable, |
171 | }; |
172 | |
173 | static int lsdc_cursor_plane_atomic_async_check(struct drm_plane *plane, |
174 | struct drm_atomic_state *state) |
175 | { |
176 | struct drm_plane_state *new_state; |
177 | struct drm_crtc_state *crtc_state; |
178 | |
179 | new_state = drm_atomic_get_new_plane_state(state, plane); |
180 | |
181 | if (!plane->state || !plane->state->fb) { |
182 | drm_dbg(plane->dev, "%s: state is NULL\n" , plane->name); |
183 | return -EINVAL; |
184 | } |
185 | |
186 | if (new_state->crtc_w != new_state->crtc_h) { |
187 | drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n" , |
188 | new_state->crtc_w, new_state->crtc_h); |
189 | return -EINVAL; |
190 | } |
191 | |
192 | if (new_state->crtc_w != 64 && new_state->crtc_w != 32) { |
193 | drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n" , |
194 | new_state->crtc_w, new_state->crtc_h); |
195 | return -EINVAL; |
196 | } |
197 | |
198 | crtc_state = drm_atomic_get_existing_crtc_state(state, crtc: new_state->crtc); |
199 | if (!crtc_state->active) |
200 | return -EINVAL; |
201 | |
202 | if (plane->state->crtc != new_state->crtc || |
203 | plane->state->src_w != new_state->src_w || |
204 | plane->state->src_h != new_state->src_h || |
205 | plane->state->crtc_w != new_state->crtc_w || |
206 | plane->state->crtc_h != new_state->crtc_h) |
207 | return -EINVAL; |
208 | |
209 | if (new_state->visible != plane->state->visible) |
210 | return -EINVAL; |
211 | |
212 | return drm_atomic_helper_check_plane_state(plane_state: plane->state, |
213 | crtc_state, |
214 | DRM_PLANE_NO_SCALING, |
215 | DRM_PLANE_NO_SCALING, |
216 | can_position: true, can_update_disabled: true); |
217 | } |
218 | |
219 | static void lsdc_cursor_plane_atomic_async_update(struct drm_plane *plane, |
220 | struct drm_atomic_state *state) |
221 | { |
222 | struct lsdc_cursor *cursor = to_lsdc_cursor(plane); |
223 | const struct lsdc_cursor_plane_ops *ops = cursor->ops; |
224 | struct drm_framebuffer *old_fb = plane->state->fb; |
225 | struct drm_framebuffer *new_fb; |
226 | struct drm_plane_state *new_state; |
227 | |
228 | new_state = drm_atomic_get_new_plane_state(state, plane); |
229 | |
230 | new_fb = plane->state->fb; |
231 | |
232 | plane->state->crtc_x = new_state->crtc_x; |
233 | plane->state->crtc_y = new_state->crtc_y; |
234 | plane->state->crtc_h = new_state->crtc_h; |
235 | plane->state->crtc_w = new_state->crtc_w; |
236 | plane->state->src_x = new_state->src_x; |
237 | plane->state->src_y = new_state->src_y; |
238 | plane->state->src_h = new_state->src_h; |
239 | plane->state->src_w = new_state->src_w; |
240 | swap(plane->state->fb, new_state->fb); |
241 | |
242 | if (new_state->visible) { |
243 | enum lsdc_cursor_size cursor_size; |
244 | |
245 | switch (new_state->crtc_w) { |
246 | case 64: |
247 | cursor_size = CURSOR_SIZE_64X64; |
248 | break; |
249 | case 32: |
250 | cursor_size = CURSOR_SIZE_32X32; |
251 | break; |
252 | default: |
253 | cursor_size = CURSOR_SIZE_32X32; |
254 | break; |
255 | } |
256 | |
257 | ops->update_position(cursor, new_state->crtc_x, new_state->crtc_y); |
258 | |
259 | ops->update_cfg(cursor, cursor_size, CURSOR_FORMAT_ARGB8888); |
260 | |
261 | if (!old_fb || old_fb != new_fb) |
262 | ops->update_bo_addr(cursor, lsdc_fb_base_addr(fb: new_fb)); |
263 | } |
264 | } |
265 | |
266 | /* ls7a1000 cursor plane helpers */ |
267 | |
268 | static int ls7a1000_cursor_plane_atomic_check(struct drm_plane *plane, |
269 | struct drm_atomic_state *state) |
270 | { |
271 | struct drm_plane_state *new_plane_state; |
272 | struct drm_crtc_state *new_crtc_state; |
273 | struct drm_crtc *crtc; |
274 | |
275 | new_plane_state = drm_atomic_get_new_plane_state(state, plane); |
276 | |
277 | crtc = new_plane_state->crtc; |
278 | if (!crtc) { |
279 | drm_dbg(plane->dev, "%s is not bind to a crtc\n" , plane->name); |
280 | return 0; |
281 | } |
282 | |
283 | if (new_plane_state->crtc_w != 32 || new_plane_state->crtc_h != 32) { |
284 | drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n" , |
285 | new_plane_state->crtc_w, new_plane_state->crtc_h); |
286 | return -EINVAL; |
287 | } |
288 | |
289 | new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); |
290 | |
291 | return drm_atomic_helper_check_plane_state(plane_state: new_plane_state, |
292 | crtc_state: new_crtc_state, |
293 | DRM_PLANE_NO_SCALING, |
294 | DRM_PLANE_NO_SCALING, |
295 | can_position: true, can_update_disabled: true); |
296 | } |
297 | |
298 | static void ls7a1000_cursor_plane_atomic_update(struct drm_plane *plane, |
299 | struct drm_atomic_state *state) |
300 | { |
301 | struct lsdc_cursor *cursor = to_lsdc_cursor(plane); |
302 | struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); |
303 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); |
304 | struct drm_framebuffer *new_fb = new_plane_state->fb; |
305 | struct drm_framebuffer *old_fb = old_plane_state->fb; |
306 | const struct lsdc_cursor_plane_ops *ops = cursor->ops; |
307 | u64 addr = lsdc_fb_base_addr(fb: new_fb); |
308 | |
309 | if (!new_plane_state->visible) |
310 | return; |
311 | |
312 | ops->update_position(cursor, new_plane_state->crtc_x, new_plane_state->crtc_y); |
313 | |
314 | if (!old_fb || old_fb != new_fb) |
315 | ops->update_bo_addr(cursor, addr); |
316 | |
317 | ops->update_cfg(cursor, CURSOR_SIZE_32X32, CURSOR_FORMAT_ARGB8888); |
318 | } |
319 | |
320 | static void ls7a1000_cursor_plane_atomic_disable(struct drm_plane *plane, |
321 | struct drm_atomic_state *state) |
322 | { |
323 | struct lsdc_cursor *cursor = to_lsdc_cursor(plane); |
324 | const struct lsdc_cursor_plane_ops *ops = cursor->ops; |
325 | |
326 | ops->update_cfg(cursor, CURSOR_SIZE_32X32, CURSOR_FORMAT_DISABLE); |
327 | } |
328 | |
329 | static const struct drm_plane_helper_funcs ls7a1000_cursor_plane_helper_funcs = { |
330 | .prepare_fb = lsdc_plane_prepare_fb, |
331 | .cleanup_fb = lsdc_plane_cleanup_fb, |
332 | .atomic_check = ls7a1000_cursor_plane_atomic_check, |
333 | .atomic_update = ls7a1000_cursor_plane_atomic_update, |
334 | .atomic_disable = ls7a1000_cursor_plane_atomic_disable, |
335 | .atomic_async_check = lsdc_cursor_plane_atomic_async_check, |
336 | .atomic_async_update = lsdc_cursor_plane_atomic_async_update, |
337 | }; |
338 | |
339 | /* ls7a2000 cursor plane helpers */ |
340 | |
341 | static int ls7a2000_cursor_plane_atomic_check(struct drm_plane *plane, |
342 | struct drm_atomic_state *state) |
343 | { |
344 | struct drm_plane_state *new_plane_state; |
345 | struct drm_crtc_state *new_crtc_state; |
346 | struct drm_crtc *crtc; |
347 | |
348 | new_plane_state = drm_atomic_get_new_plane_state(state, plane); |
349 | |
350 | crtc = new_plane_state->crtc; |
351 | if (!crtc) { |
352 | drm_dbg(plane->dev, "%s is not bind to a crtc\n" , plane->name); |
353 | return 0; |
354 | } |
355 | |
356 | if (new_plane_state->crtc_w != new_plane_state->crtc_h) { |
357 | drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n" , |
358 | new_plane_state->crtc_w, new_plane_state->crtc_h); |
359 | return -EINVAL; |
360 | } |
361 | |
362 | if (new_plane_state->crtc_w != 64 && new_plane_state->crtc_w != 32) { |
363 | drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n" , |
364 | new_plane_state->crtc_w, new_plane_state->crtc_h); |
365 | return -EINVAL; |
366 | } |
367 | |
368 | new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); |
369 | |
370 | return drm_atomic_helper_check_plane_state(plane_state: new_plane_state, |
371 | crtc_state: new_crtc_state, |
372 | DRM_PLANE_NO_SCALING, |
373 | DRM_PLANE_NO_SCALING, |
374 | can_position: true, can_update_disabled: true); |
375 | } |
376 | |
377 | /* Update the format, size and location of the cursor */ |
378 | |
379 | static void ls7a2000_cursor_plane_atomic_update(struct drm_plane *plane, |
380 | struct drm_atomic_state *state) |
381 | { |
382 | struct lsdc_cursor *cursor = to_lsdc_cursor(plane); |
383 | struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); |
384 | struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane); |
385 | struct drm_framebuffer *new_fb = new_plane_state->fb; |
386 | struct drm_framebuffer *old_fb = old_plane_state->fb; |
387 | const struct lsdc_cursor_plane_ops *ops = cursor->ops; |
388 | enum lsdc_cursor_size cursor_size; |
389 | |
390 | if (!new_plane_state->visible) |
391 | return; |
392 | |
393 | ops->update_position(cursor, new_plane_state->crtc_x, new_plane_state->crtc_y); |
394 | |
395 | if (!old_fb || new_fb != old_fb) { |
396 | u64 addr = lsdc_fb_base_addr(fb: new_fb); |
397 | |
398 | ops->update_bo_addr(cursor, addr); |
399 | } |
400 | |
401 | switch (new_plane_state->crtc_w) { |
402 | case 64: |
403 | cursor_size = CURSOR_SIZE_64X64; |
404 | break; |
405 | case 32: |
406 | cursor_size = CURSOR_SIZE_32X32; |
407 | break; |
408 | default: |
409 | cursor_size = CURSOR_SIZE_64X64; |
410 | break; |
411 | } |
412 | |
413 | ops->update_cfg(cursor, cursor_size, CURSOR_FORMAT_ARGB8888); |
414 | } |
415 | |
416 | static void ls7a2000_cursor_plane_atomic_disable(struct drm_plane *plane, |
417 | struct drm_atomic_state *state) |
418 | { |
419 | struct lsdc_cursor *cursor = to_lsdc_cursor(plane); |
420 | const struct lsdc_cursor_plane_ops *hw_ops = cursor->ops; |
421 | |
422 | hw_ops->update_cfg(cursor, CURSOR_SIZE_64X64, CURSOR_FORMAT_DISABLE); |
423 | } |
424 | |
425 | static const struct drm_plane_helper_funcs ls7a2000_cursor_plane_helper_funcs = { |
426 | .prepare_fb = lsdc_plane_prepare_fb, |
427 | .cleanup_fb = lsdc_plane_cleanup_fb, |
428 | .atomic_check = ls7a2000_cursor_plane_atomic_check, |
429 | .atomic_update = ls7a2000_cursor_plane_atomic_update, |
430 | .atomic_disable = ls7a2000_cursor_plane_atomic_disable, |
431 | .atomic_async_check = lsdc_cursor_plane_atomic_async_check, |
432 | .atomic_async_update = lsdc_cursor_plane_atomic_async_update, |
433 | }; |
434 | |
435 | static void lsdc_plane_atomic_print_state(struct drm_printer *p, |
436 | const struct drm_plane_state *state) |
437 | { |
438 | struct drm_framebuffer *fb = state->fb; |
439 | u64 addr; |
440 | |
441 | if (!fb) |
442 | return; |
443 | |
444 | addr = lsdc_fb_base_addr(fb); |
445 | |
446 | drm_printf(p, f: "\tdma addr=%llx\n" , addr); |
447 | } |
448 | |
449 | static const struct drm_plane_funcs lsdc_plane_funcs = { |
450 | .update_plane = drm_atomic_helper_update_plane, |
451 | .disable_plane = drm_atomic_helper_disable_plane, |
452 | .destroy = drm_plane_cleanup, |
453 | .reset = drm_atomic_helper_plane_reset, |
454 | .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
455 | .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
456 | .atomic_print_state = lsdc_plane_atomic_print_state, |
457 | }; |
458 | |
459 | /* Primary plane 0 hardware related ops */ |
460 | |
461 | static void lsdc_primary0_update_fb_addr(struct lsdc_primary *primary, u64 addr) |
462 | { |
463 | struct lsdc_device *ldev = primary->ldev; |
464 | u32 status; |
465 | u32 lo, hi; |
466 | |
467 | /* 40-bit width physical address bus */ |
468 | lo = addr & 0xFFFFFFFF; |
469 | hi = (addr >> 32) & 0xFF; |
470 | |
471 | status = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); |
472 | if (status & FB_REG_IN_USING) { |
473 | lsdc_wreg32(ldev, LSDC_CRTC0_FB1_ADDR_LO_REG, val: lo); |
474 | lsdc_wreg32(ldev, LSDC_CRTC0_FB1_ADDR_HI_REG, val: hi); |
475 | } else { |
476 | lsdc_wreg32(ldev, LSDC_CRTC0_FB0_ADDR_LO_REG, val: lo); |
477 | lsdc_wreg32(ldev, LSDC_CRTC0_FB0_ADDR_HI_REG, val: hi); |
478 | } |
479 | } |
480 | |
481 | static void lsdc_primary0_update_fb_stride(struct lsdc_primary *primary, u32 stride) |
482 | { |
483 | struct lsdc_device *ldev = primary->ldev; |
484 | |
485 | lsdc_wreg32(ldev, LSDC_CRTC0_STRIDE_REG, val: stride); |
486 | } |
487 | |
488 | static void lsdc_primary0_update_fb_format(struct lsdc_primary *primary, |
489 | const struct drm_format_info *format) |
490 | { |
491 | struct lsdc_device *ldev = primary->ldev; |
492 | u32 status; |
493 | |
494 | status = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG); |
495 | |
496 | /* |
497 | * TODO: add RGB565 support, only support XRBG8888 at present |
498 | */ |
499 | status &= ~CFG_PIX_FMT_MASK; |
500 | status |= LSDC_PF_XRGB8888; |
501 | |
502 | lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, val: status); |
503 | } |
504 | |
505 | /* Primary plane 1 hardware related ops */ |
506 | |
507 | static void lsdc_primary1_update_fb_addr(struct lsdc_primary *primary, u64 addr) |
508 | { |
509 | struct lsdc_device *ldev = primary->ldev; |
510 | u32 status; |
511 | u32 lo, hi; |
512 | |
513 | /* 40-bit width physical address bus */ |
514 | lo = addr & 0xFFFFFFFF; |
515 | hi = (addr >> 32) & 0xFF; |
516 | |
517 | status = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); |
518 | if (status & FB_REG_IN_USING) { |
519 | lsdc_wreg32(ldev, LSDC_CRTC1_FB1_ADDR_LO_REG, val: lo); |
520 | lsdc_wreg32(ldev, LSDC_CRTC1_FB1_ADDR_HI_REG, val: hi); |
521 | } else { |
522 | lsdc_wreg32(ldev, LSDC_CRTC1_FB0_ADDR_LO_REG, val: lo); |
523 | lsdc_wreg32(ldev, LSDC_CRTC1_FB0_ADDR_HI_REG, val: hi); |
524 | } |
525 | } |
526 | |
527 | static void lsdc_primary1_update_fb_stride(struct lsdc_primary *primary, u32 stride) |
528 | { |
529 | struct lsdc_device *ldev = primary->ldev; |
530 | |
531 | lsdc_wreg32(ldev, LSDC_CRTC1_STRIDE_REG, val: stride); |
532 | } |
533 | |
534 | static void lsdc_primary1_update_fb_format(struct lsdc_primary *primary, |
535 | const struct drm_format_info *format) |
536 | { |
537 | struct lsdc_device *ldev = primary->ldev; |
538 | u32 status; |
539 | |
540 | status = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG); |
541 | |
542 | /* |
543 | * TODO: add RGB565 support, only support XRBG8888 at present |
544 | */ |
545 | status &= ~CFG_PIX_FMT_MASK; |
546 | status |= LSDC_PF_XRGB8888; |
547 | |
548 | lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, val: status); |
549 | } |
550 | |
551 | static const struct lsdc_primary_plane_ops lsdc_primary_plane_hw_ops[2] = { |
552 | { |
553 | .update_fb_addr = lsdc_primary0_update_fb_addr, |
554 | .update_fb_stride = lsdc_primary0_update_fb_stride, |
555 | .update_fb_format = lsdc_primary0_update_fb_format, |
556 | }, |
557 | { |
558 | .update_fb_addr = lsdc_primary1_update_fb_addr, |
559 | .update_fb_stride = lsdc_primary1_update_fb_stride, |
560 | .update_fb_format = lsdc_primary1_update_fb_format, |
561 | }, |
562 | }; |
563 | |
564 | /* |
565 | * Update location, format, enable and disable state of the cursor, |
566 | * For those who have two hardware cursor, let cursor 0 is attach to CRTC-0, |
567 | * cursor 1 is attach to CRTC-1. Compositing the primary plane and cursor |
568 | * plane is automatically done by hardware, the cursor is alway on the top of |
569 | * the primary plane. In other word, z-order is fixed in hardware and cannot |
570 | * be changed. For those old DC who has only one hardware cursor, we made it |
571 | * shared by the two screen, this works on extend screen mode. |
572 | */ |
573 | |
574 | /* cursor plane 0 (for pipe 0) related hardware ops */ |
575 | |
576 | static void lsdc_cursor0_update_bo_addr(struct lsdc_cursor *cursor, u64 addr) |
577 | { |
578 | struct lsdc_device *ldev = cursor->ldev; |
579 | |
580 | /* 40-bit width physical address bus */ |
581 | lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_HI_REG, val: (addr >> 32) & 0xFF); |
582 | lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_LO_REG, val: addr); |
583 | } |
584 | |
585 | static void lsdc_cursor0_update_position(struct lsdc_cursor *cursor, int x, int y) |
586 | { |
587 | struct lsdc_device *ldev = cursor->ldev; |
588 | |
589 | if (x < 0) |
590 | x = 0; |
591 | |
592 | if (y < 0) |
593 | y = 0; |
594 | |
595 | lsdc_wreg32(ldev, LSDC_CURSOR0_POSITION_REG, val: (y << 16) | x); |
596 | } |
597 | |
598 | static void lsdc_cursor0_update_cfg(struct lsdc_cursor *cursor, |
599 | enum lsdc_cursor_size cursor_size, |
600 | enum lsdc_cursor_format fmt) |
601 | { |
602 | struct lsdc_device *ldev = cursor->ldev; |
603 | u32 cfg; |
604 | |
605 | cfg = CURSOR_ON_CRTC0 << CURSOR_LOCATION_SHIFT | |
606 | cursor_size << CURSOR_SIZE_SHIFT | |
607 | fmt << CURSOR_FORMAT_SHIFT; |
608 | |
609 | lsdc_wreg32(ldev, LSDC_CURSOR0_CFG_REG, val: cfg); |
610 | } |
611 | |
612 | /* cursor plane 1 (for pipe 1) related hardware ops */ |
613 | |
614 | static void lsdc_cursor1_update_bo_addr(struct lsdc_cursor *cursor, u64 addr) |
615 | { |
616 | struct lsdc_device *ldev = cursor->ldev; |
617 | |
618 | /* 40-bit width physical address bus */ |
619 | lsdc_wreg32(ldev, LSDC_CURSOR1_ADDR_HI_REG, val: (addr >> 32) & 0xFF); |
620 | lsdc_wreg32(ldev, LSDC_CURSOR1_ADDR_LO_REG, val: addr); |
621 | } |
622 | |
623 | static void lsdc_cursor1_update_position(struct lsdc_cursor *cursor, int x, int y) |
624 | { |
625 | struct lsdc_device *ldev = cursor->ldev; |
626 | |
627 | if (x < 0) |
628 | x = 0; |
629 | |
630 | if (y < 0) |
631 | y = 0; |
632 | |
633 | lsdc_wreg32(ldev, LSDC_CURSOR1_POSITION_REG, val: (y << 16) | x); |
634 | } |
635 | |
636 | static void lsdc_cursor1_update_cfg(struct lsdc_cursor *cursor, |
637 | enum lsdc_cursor_size cursor_size, |
638 | enum lsdc_cursor_format fmt) |
639 | { |
640 | struct lsdc_device *ldev = cursor->ldev; |
641 | u32 cfg; |
642 | |
643 | cfg = CURSOR_ON_CRTC1 << CURSOR_LOCATION_SHIFT | |
644 | cursor_size << CURSOR_SIZE_SHIFT | |
645 | fmt << CURSOR_FORMAT_SHIFT; |
646 | |
647 | lsdc_wreg32(ldev, LSDC_CURSOR1_CFG_REG, val: cfg); |
648 | } |
649 | |
650 | /* The hardware cursors become normal since ls7a2000/ls2k2000 */ |
651 | |
652 | static const struct lsdc_cursor_plane_ops ls7a2000_cursor_hw_ops[2] = { |
653 | { |
654 | .update_bo_addr = lsdc_cursor0_update_bo_addr, |
655 | .update_cfg = lsdc_cursor0_update_cfg, |
656 | .update_position = lsdc_cursor0_update_position, |
657 | }, |
658 | { |
659 | .update_bo_addr = lsdc_cursor1_update_bo_addr, |
660 | .update_cfg = lsdc_cursor1_update_cfg, |
661 | .update_position = lsdc_cursor1_update_position, |
662 | }, |
663 | }; |
664 | |
665 | /* Quirks for cursor 1, only for old loongson display controller */ |
666 | |
667 | static void lsdc_cursor1_update_bo_addr_quirk(struct lsdc_cursor *cursor, u64 addr) |
668 | { |
669 | struct lsdc_device *ldev = cursor->ldev; |
670 | |
671 | /* 40-bit width physical address bus */ |
672 | lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_HI_REG, val: (addr >> 32) & 0xFF); |
673 | lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_LO_REG, val: addr); |
674 | } |
675 | |
676 | static void lsdc_cursor1_update_position_quirk(struct lsdc_cursor *cursor, int x, int y) |
677 | { |
678 | struct lsdc_device *ldev = cursor->ldev; |
679 | |
680 | if (x < 0) |
681 | x = 0; |
682 | |
683 | if (y < 0) |
684 | y = 0; |
685 | |
686 | lsdc_wreg32(ldev, LSDC_CURSOR0_POSITION_REG, val: (y << 16) | x); |
687 | } |
688 | |
689 | static void lsdc_cursor1_update_cfg_quirk(struct lsdc_cursor *cursor, |
690 | enum lsdc_cursor_size cursor_size, |
691 | enum lsdc_cursor_format fmt) |
692 | { |
693 | struct lsdc_device *ldev = cursor->ldev; |
694 | u32 cfg; |
695 | |
696 | cfg = CURSOR_ON_CRTC1 << CURSOR_LOCATION_SHIFT | |
697 | cursor_size << CURSOR_SIZE_SHIFT | |
698 | fmt << CURSOR_FORMAT_SHIFT; |
699 | |
700 | lsdc_wreg32(ldev, LSDC_CURSOR0_CFG_REG, val: cfg); |
701 | } |
702 | |
703 | /* |
704 | * The unforgiving LS7A1000/LS2K1000 has only one hardware cursors plane |
705 | */ |
706 | static const struct lsdc_cursor_plane_ops ls7a1000_cursor_hw_ops[2] = { |
707 | { |
708 | .update_bo_addr = lsdc_cursor0_update_bo_addr, |
709 | .update_cfg = lsdc_cursor0_update_cfg, |
710 | .update_position = lsdc_cursor0_update_position, |
711 | }, |
712 | { |
713 | .update_bo_addr = lsdc_cursor1_update_bo_addr_quirk, |
714 | .update_cfg = lsdc_cursor1_update_cfg_quirk, |
715 | .update_position = lsdc_cursor1_update_position_quirk, |
716 | }, |
717 | }; |
718 | |
719 | int lsdc_primary_plane_init(struct drm_device *ddev, |
720 | struct drm_plane *plane, |
721 | unsigned int index) |
722 | { |
723 | struct lsdc_primary *primary = to_lsdc_primary(plane); |
724 | int ret; |
725 | |
726 | ret = drm_universal_plane_init(dev: ddev, plane, possible_crtcs: 1 << index, |
727 | funcs: &lsdc_plane_funcs, |
728 | formats: lsdc_primary_formats, |
729 | ARRAY_SIZE(lsdc_primary_formats), |
730 | format_modifiers: lsdc_fb_format_modifiers, |
731 | type: DRM_PLANE_TYPE_PRIMARY, |
732 | name: "ls-primary-plane-%u" , index); |
733 | if (ret) |
734 | return ret; |
735 | |
736 | drm_plane_helper_add(plane, funcs: &lsdc_primary_helper_funcs); |
737 | |
738 | primary->ldev = to_lsdc(ddev); |
739 | primary->ops = &lsdc_primary_plane_hw_ops[index]; |
740 | |
741 | return 0; |
742 | } |
743 | |
744 | int ls7a1000_cursor_plane_init(struct drm_device *ddev, |
745 | struct drm_plane *plane, |
746 | unsigned int index) |
747 | { |
748 | struct lsdc_cursor *cursor = to_lsdc_cursor(plane); |
749 | int ret; |
750 | |
751 | ret = drm_universal_plane_init(dev: ddev, plane, possible_crtcs: 1 << index, |
752 | funcs: &lsdc_plane_funcs, |
753 | formats: lsdc_cursor_formats, |
754 | ARRAY_SIZE(lsdc_cursor_formats), |
755 | format_modifiers: lsdc_fb_format_modifiers, |
756 | type: DRM_PLANE_TYPE_CURSOR, |
757 | name: "ls-cursor-plane-%u" , index); |
758 | if (ret) |
759 | return ret; |
760 | |
761 | cursor->ldev = to_lsdc(ddev); |
762 | cursor->ops = &ls7a1000_cursor_hw_ops[index]; |
763 | |
764 | drm_plane_helper_add(plane, funcs: &ls7a1000_cursor_plane_helper_funcs); |
765 | |
766 | return 0; |
767 | } |
768 | |
769 | int ls7a2000_cursor_plane_init(struct drm_device *ddev, |
770 | struct drm_plane *plane, |
771 | unsigned int index) |
772 | { |
773 | struct lsdc_cursor *cursor = to_lsdc_cursor(plane); |
774 | int ret; |
775 | |
776 | ret = drm_universal_plane_init(dev: ddev, plane, possible_crtcs: 1 << index, |
777 | funcs: &lsdc_plane_funcs, |
778 | formats: lsdc_cursor_formats, |
779 | ARRAY_SIZE(lsdc_cursor_formats), |
780 | format_modifiers: lsdc_fb_format_modifiers, |
781 | type: DRM_PLANE_TYPE_CURSOR, |
782 | name: "ls-cursor-plane-%u" , index); |
783 | if (ret) |
784 | return ret; |
785 | |
786 | cursor->ldev = to_lsdc(ddev); |
787 | cursor->ops = &ls7a2000_cursor_hw_ops[index]; |
788 | |
789 | drm_plane_helper_add(plane, funcs: &ls7a2000_cursor_plane_helper_funcs); |
790 | |
791 | return 0; |
792 | } |
793 | |