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
17static const u32 lsdc_primary_formats[] = {
18	DRM_FORMAT_XRGB8888,
19};
20
21static const u32 lsdc_cursor_formats[] = {
22	DRM_FORMAT_ARGB8888,
23};
24
25static const u64 lsdc_fb_format_modifiers[] = {
26	DRM_FORMAT_MOD_LINEAR,
27	DRM_FORMAT_MOD_INVALID
28};
29
30static 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
41static 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
49static 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
68static 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
88static 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
99static 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
137static 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
165static 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
173static 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
219static 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
268static 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
298static 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
320static 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
329static 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
341static 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
379static 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
416static 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
425static 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
435static 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
449static 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
461static 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
481static 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
488static 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
507static 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
527static 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
534static 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
551static 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
576static 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
585static 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
598static 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
614static 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
623static 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
636static 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
652static 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
667static 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
676static 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
689static 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 */
706static 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
719int 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
744int 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
769int 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