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