File Index
Files and directories
Sign in
FILE

lsdc_plane.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/loongson/lsdc_plane.c C
  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
 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						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
221static 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
270static 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
300static 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
322static 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
331static 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
343static 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
381static 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
418static 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
427static 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
437static 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
451static 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
463static 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
483static 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
490static 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
509static 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
529static 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
536static 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
553static 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
578static 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
587static 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
600static 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
616static 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
625static 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
638static 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
654static 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
669static 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
678static 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
691static 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 */
708static 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
721int 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
746int 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
771int 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}
~/ Read-only file preview.