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