xref: /linux/drivers/gpu/drm/loongson/lsdc_plane.c (revision 24168c5e6dfbdd5b414f048f47f75d64533296ca)
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(fb->dev);
44 	struct lsdc_bo *lbo = gem_to_lsdc_bo(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(new_plane_state,
62 						   new_crtc_state,
63 						   DRM_PLANE_NO_SCALING,
64 						   DRM_PLANE_NO_SCALING,
65 						   false, 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(new_fb);
78 
79 	fb_addr += lsdc_get_fb_offset(new_fb, 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(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_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, 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(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, 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,
213 						   crtc_state,
214 						   DRM_PLANE_NO_SCALING,
215 						   DRM_PLANE_NO_SCALING,
216 						   true, 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(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(new_plane_state,
292 						   new_crtc_state,
293 						   DRM_PLANE_NO_SCALING,
294 						   DRM_PLANE_NO_SCALING,
295 						   true, 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(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(new_plane_state,
371 						   new_crtc_state,
372 						   DRM_PLANE_NO_SCALING,
373 						   DRM_PLANE_NO_SCALING,
374 						   true, 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(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, "\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, lo);
474 		lsdc_wreg32(ldev, LSDC_CRTC0_FB1_ADDR_HI_REG, hi);
475 	} else {
476 		lsdc_wreg32(ldev, LSDC_CRTC0_FB0_ADDR_LO_REG, lo);
477 		lsdc_wreg32(ldev, LSDC_CRTC0_FB0_ADDR_HI_REG, 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, 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, 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, lo);
520 		lsdc_wreg32(ldev, LSDC_CRTC1_FB1_ADDR_HI_REG, hi);
521 	} else {
522 		lsdc_wreg32(ldev, LSDC_CRTC1_FB0_ADDR_LO_REG, lo);
523 		lsdc_wreg32(ldev, LSDC_CRTC1_FB0_ADDR_HI_REG, 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, 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, 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, (addr >> 32) & 0xFF);
582 	lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_LO_REG, 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, (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, 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, (addr >> 32) & 0xFF);
620 	lsdc_wreg32(ldev, LSDC_CURSOR1_ADDR_LO_REG, 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, (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, 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, (addr >> 32) & 0xFF);
673 	lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_LO_REG, 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, (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, 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(ddev, plane, 1 << index,
727 				       &lsdc_plane_funcs,
728 				       lsdc_primary_formats,
729 				       ARRAY_SIZE(lsdc_primary_formats),
730 				       lsdc_fb_format_modifiers,
731 				       DRM_PLANE_TYPE_PRIMARY,
732 				       "ls-primary-plane-%u", index);
733 	if (ret)
734 		return ret;
735 
736 	drm_plane_helper_add(plane, &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(ddev, plane, 1 << index,
752 				       &lsdc_plane_funcs,
753 				       lsdc_cursor_formats,
754 				       ARRAY_SIZE(lsdc_cursor_formats),
755 				       lsdc_fb_format_modifiers,
756 				       DRM_PLANE_TYPE_CURSOR,
757 				       "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, &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(ddev, plane, 1 << index,
777 				       &lsdc_plane_funcs,
778 				       lsdc_cursor_formats,
779 				       ARRAY_SIZE(lsdc_cursor_formats),
780 				       lsdc_fb_format_modifiers,
781 				       DRM_PLANE_TYPE_CURSOR,
782 				       "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, &ls7a2000_cursor_plane_helper_funcs);
790 
791 	return 0;
792 }
793