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
18static const u32 lsdc_primary_formats[] = {
19	DRM_FORMAT_XRGB8888,
20};
21
22static const u32 lsdc_cursor_formats[] = {
23	DRM_FORMAT_ARGB8888,
24};
25
26static const u64 lsdc_fb_format_modifiers[] = {
27	DRM_FORMAT_MOD_LINEAR,
28	DRM_FORMAT_MOD_INVALID
29};
30
31static 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
42static 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
50static 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
69static 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
89static 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
100static 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
138static 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
166static 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
174static 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
220static 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
269static 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
299static 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
321static 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
330static 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
342static 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
380static 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
417static 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
426static 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
436static 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
450static 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
462static 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
482static 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
489static 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
508static 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
528static 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
535static 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
552static 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
577static 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
586static 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
599static 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
615static 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
624static 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
637static 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
653static 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
668static 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
677static 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
690static 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 */
707static 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
720int 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
745int 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
770int 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