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