162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2023 Loongson Technology Corporation Limited
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/delay.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <drm/drm_atomic.h>
962306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
1062306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
1162306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h>
1262306a36Sopenharmony_ci#include <drm/drm_plane_helper.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "lsdc_drv.h"
1562306a36Sopenharmony_ci#include "lsdc_regs.h"
1662306a36Sopenharmony_ci#include "lsdc_ttm.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic const u32 lsdc_primary_formats[] = {
1962306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic const u32 lsdc_cursor_formats[] = {
2362306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic const u64 lsdc_fb_format_modifiers[] = {
2762306a36Sopenharmony_ci	DRM_FORMAT_MOD_LINEAR,
2862306a36Sopenharmony_ci	DRM_FORMAT_MOD_INVALID
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic unsigned int lsdc_get_fb_offset(struct drm_framebuffer *fb,
3262306a36Sopenharmony_ci				       struct drm_plane_state *state)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	unsigned int offset = fb->offsets[0];
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	offset += fb->format->cpp[0] * (state->src_x >> 16);
3762306a36Sopenharmony_ci	offset += fb->pitches[0] * (state->src_y >> 16);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	return offset;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic u64 lsdc_fb_base_addr(struct drm_framebuffer *fb)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	struct lsdc_device *ldev = to_lsdc(fb->dev);
4562306a36Sopenharmony_ci	struct lsdc_bo *lbo = gem_to_lsdc_bo(fb->obj[0]);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return lsdc_bo_gpu_offset(lbo) + ldev->vram_base;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int lsdc_primary_atomic_check(struct drm_plane *plane,
5162306a36Sopenharmony_ci				     struct drm_atomic_state *state)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
5462306a36Sopenharmony_ci	struct drm_crtc *crtc = new_plane_state->crtc;
5562306a36Sopenharmony_ci	struct drm_crtc_state *new_crtc_state;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (!crtc)
5862306a36Sopenharmony_ci		return 0;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return drm_atomic_helper_check_plane_state(new_plane_state,
6362306a36Sopenharmony_ci						   new_crtc_state,
6462306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
6562306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
6662306a36Sopenharmony_ci						   false, true);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void lsdc_primary_atomic_update(struct drm_plane *plane,
7062306a36Sopenharmony_ci				       struct drm_atomic_state *state)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	struct lsdc_primary *primary = to_lsdc_primary(plane);
7362306a36Sopenharmony_ci	const struct lsdc_primary_plane_ops *ops = primary->ops;
7462306a36Sopenharmony_ci	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
7562306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
7662306a36Sopenharmony_ci	struct drm_framebuffer *new_fb = new_plane_state->fb;
7762306a36Sopenharmony_ci	struct drm_framebuffer *old_fb = old_plane_state->fb;
7862306a36Sopenharmony_ci	u64 fb_addr = lsdc_fb_base_addr(new_fb);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	fb_addr += lsdc_get_fb_offset(new_fb, new_plane_state);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	ops->update_fb_addr(primary, fb_addr);
8362306a36Sopenharmony_ci	ops->update_fb_stride(primary, new_fb->pitches[0]);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (!old_fb || old_fb->format != new_fb->format)
8662306a36Sopenharmony_ci		ops->update_fb_format(primary, new_fb->format);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void lsdc_primary_atomic_disable(struct drm_plane *plane,
9062306a36Sopenharmony_ci					struct drm_atomic_state *state)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	/*
9362306a36Sopenharmony_ci	 * Do nothing, just prevent call into atomic_update().
9462306a36Sopenharmony_ci	 * Writing the format as LSDC_PF_NONE can disable the primary,
9562306a36Sopenharmony_ci	 * But it seems not necessary...
9662306a36Sopenharmony_ci	 */
9762306a36Sopenharmony_ci	drm_dbg(plane->dev, "%s disabled\n", plane->name);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int lsdc_plane_prepare_fb(struct drm_plane *plane,
10162306a36Sopenharmony_ci				 struct drm_plane_state *new_state)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct drm_framebuffer *fb = new_state->fb;
10462306a36Sopenharmony_ci	struct lsdc_bo *lbo;
10562306a36Sopenharmony_ci	u64 gpu_vaddr;
10662306a36Sopenharmony_ci	int ret;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	if (!fb)
10962306a36Sopenharmony_ci		return 0;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	lbo = gem_to_lsdc_bo(fb->obj[0]);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	ret = lsdc_bo_reserve(lbo);
11462306a36Sopenharmony_ci	if (unlikely(ret)) {
11562306a36Sopenharmony_ci		drm_err(plane->dev, "bo %p reserve failed\n", lbo);
11662306a36Sopenharmony_ci		return ret;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	ret = lsdc_bo_pin(lbo, LSDC_GEM_DOMAIN_VRAM, &gpu_vaddr);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	lsdc_bo_unreserve(lbo);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (unlikely(ret)) {
12462306a36Sopenharmony_ci		drm_err(plane->dev, "bo %p pin failed\n", lbo);
12562306a36Sopenharmony_ci		return ret;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	lsdc_bo_ref(lbo);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (plane->type != DRM_PLANE_TYPE_CURSOR)
13162306a36Sopenharmony_ci		drm_dbg(plane->dev,
13262306a36Sopenharmony_ci			"%s[%p] pin at 0x%llx, bo size: %zu\n",
13362306a36Sopenharmony_ci			plane->name, lbo, gpu_vaddr, lsdc_bo_size(lbo));
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	return drm_gem_plane_helper_prepare_fb(plane, new_state);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic void lsdc_plane_cleanup_fb(struct drm_plane *plane,
13962306a36Sopenharmony_ci				  struct drm_plane_state *old_state)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct drm_framebuffer *fb = old_state->fb;
14262306a36Sopenharmony_ci	struct lsdc_bo *lbo;
14362306a36Sopenharmony_ci	int ret;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (!fb)
14662306a36Sopenharmony_ci		return;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	lbo = gem_to_lsdc_bo(fb->obj[0]);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	ret = lsdc_bo_reserve(lbo);
15162306a36Sopenharmony_ci	if (unlikely(ret)) {
15262306a36Sopenharmony_ci		drm_err(plane->dev, "%p reserve failed\n", lbo);
15362306a36Sopenharmony_ci		return;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	lsdc_bo_unpin(lbo);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	lsdc_bo_unreserve(lbo);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	lsdc_bo_unref(lbo);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (plane->type != DRM_PLANE_TYPE_CURSOR)
16362306a36Sopenharmony_ci		drm_dbg(plane->dev, "%s unpin\n", plane->name);
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs lsdc_primary_helper_funcs = {
16762306a36Sopenharmony_ci	.prepare_fb = lsdc_plane_prepare_fb,
16862306a36Sopenharmony_ci	.cleanup_fb = lsdc_plane_cleanup_fb,
16962306a36Sopenharmony_ci	.atomic_check = lsdc_primary_atomic_check,
17062306a36Sopenharmony_ci	.atomic_update = lsdc_primary_atomic_update,
17162306a36Sopenharmony_ci	.atomic_disable = lsdc_primary_atomic_disable,
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int lsdc_cursor_plane_atomic_async_check(struct drm_plane *plane,
17562306a36Sopenharmony_ci						struct drm_atomic_state *state)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct drm_plane_state *new_state;
17862306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	new_state = drm_atomic_get_new_plane_state(state, plane);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (!plane->state || !plane->state->fb) {
18362306a36Sopenharmony_ci		drm_dbg(plane->dev, "%s: state is NULL\n", plane->name);
18462306a36Sopenharmony_ci		return -EINVAL;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (new_state->crtc_w != new_state->crtc_h) {
18862306a36Sopenharmony_ci		drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n",
18962306a36Sopenharmony_ci			new_state->crtc_w, new_state->crtc_h);
19062306a36Sopenharmony_ci		return -EINVAL;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (new_state->crtc_w != 64 && new_state->crtc_w != 32) {
19462306a36Sopenharmony_ci		drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n",
19562306a36Sopenharmony_ci			new_state->crtc_w, new_state->crtc_h);
19662306a36Sopenharmony_ci		return -EINVAL;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc);
20062306a36Sopenharmony_ci	if (!crtc_state->active)
20162306a36Sopenharmony_ci		return -EINVAL;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	if (plane->state->crtc != new_state->crtc ||
20462306a36Sopenharmony_ci	    plane->state->src_w != new_state->src_w ||
20562306a36Sopenharmony_ci	    plane->state->src_h != new_state->src_h ||
20662306a36Sopenharmony_ci	    plane->state->crtc_w != new_state->crtc_w ||
20762306a36Sopenharmony_ci	    plane->state->crtc_h != new_state->crtc_h)
20862306a36Sopenharmony_ci		return -EINVAL;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (new_state->visible != plane->state->visible)
21162306a36Sopenharmony_ci		return -EINVAL;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	return drm_atomic_helper_check_plane_state(plane->state,
21462306a36Sopenharmony_ci						   crtc_state,
21562306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
21662306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
21762306a36Sopenharmony_ci						   true, true);
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic void lsdc_cursor_plane_atomic_async_update(struct drm_plane *plane,
22162306a36Sopenharmony_ci						  struct drm_atomic_state *state)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct lsdc_cursor *cursor = to_lsdc_cursor(plane);
22462306a36Sopenharmony_ci	const struct lsdc_cursor_plane_ops *ops = cursor->ops;
22562306a36Sopenharmony_ci	struct drm_framebuffer *old_fb = plane->state->fb;
22662306a36Sopenharmony_ci	struct drm_framebuffer *new_fb;
22762306a36Sopenharmony_ci	struct drm_plane_state *new_state;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	new_state = drm_atomic_get_new_plane_state(state, plane);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	new_fb = plane->state->fb;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	plane->state->crtc_x = new_state->crtc_x;
23462306a36Sopenharmony_ci	plane->state->crtc_y = new_state->crtc_y;
23562306a36Sopenharmony_ci	plane->state->crtc_h = new_state->crtc_h;
23662306a36Sopenharmony_ci	plane->state->crtc_w = new_state->crtc_w;
23762306a36Sopenharmony_ci	plane->state->src_x = new_state->src_x;
23862306a36Sopenharmony_ci	plane->state->src_y = new_state->src_y;
23962306a36Sopenharmony_ci	plane->state->src_h = new_state->src_h;
24062306a36Sopenharmony_ci	plane->state->src_w = new_state->src_w;
24162306a36Sopenharmony_ci	swap(plane->state->fb, new_state->fb);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (new_state->visible) {
24462306a36Sopenharmony_ci		enum lsdc_cursor_size cursor_size;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci		switch (new_state->crtc_w) {
24762306a36Sopenharmony_ci		case 64:
24862306a36Sopenharmony_ci			cursor_size = CURSOR_SIZE_64X64;
24962306a36Sopenharmony_ci			break;
25062306a36Sopenharmony_ci		case 32:
25162306a36Sopenharmony_ci			cursor_size = CURSOR_SIZE_32X32;
25262306a36Sopenharmony_ci			break;
25362306a36Sopenharmony_ci		default:
25462306a36Sopenharmony_ci			cursor_size = CURSOR_SIZE_32X32;
25562306a36Sopenharmony_ci			break;
25662306a36Sopenharmony_ci		}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		ops->update_position(cursor, new_state->crtc_x, new_state->crtc_y);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		ops->update_cfg(cursor, cursor_size, CURSOR_FORMAT_ARGB8888);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		if (!old_fb || old_fb != new_fb)
26362306a36Sopenharmony_ci			ops->update_bo_addr(cursor, lsdc_fb_base_addr(new_fb));
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci/* ls7a1000 cursor plane helpers */
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic int ls7a1000_cursor_plane_atomic_check(struct drm_plane *plane,
27062306a36Sopenharmony_ci					      struct drm_atomic_state *state)
27162306a36Sopenharmony_ci{
27262306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state;
27362306a36Sopenharmony_ci	struct drm_crtc_state *new_crtc_state;
27462306a36Sopenharmony_ci	struct drm_crtc *crtc;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	new_plane_state = drm_atomic_get_new_plane_state(state, plane);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	crtc = new_plane_state->crtc;
27962306a36Sopenharmony_ci	if (!crtc) {
28062306a36Sopenharmony_ci		drm_dbg(plane->dev, "%s is not bind to a crtc\n", plane->name);
28162306a36Sopenharmony_ci		return 0;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	if (new_plane_state->crtc_w != 32 || new_plane_state->crtc_h != 32) {
28562306a36Sopenharmony_ci		drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n",
28662306a36Sopenharmony_ci			new_plane_state->crtc_w, new_plane_state->crtc_h);
28762306a36Sopenharmony_ci		return -EINVAL;
28862306a36Sopenharmony_ci	}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	return drm_atomic_helper_check_plane_state(new_plane_state,
29362306a36Sopenharmony_ci						   new_crtc_state,
29462306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
29562306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
29662306a36Sopenharmony_ci						   true, true);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic void ls7a1000_cursor_plane_atomic_update(struct drm_plane *plane,
30062306a36Sopenharmony_ci						struct drm_atomic_state *state)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct lsdc_cursor *cursor = to_lsdc_cursor(plane);
30362306a36Sopenharmony_ci	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
30462306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
30562306a36Sopenharmony_ci	struct drm_framebuffer *new_fb = new_plane_state->fb;
30662306a36Sopenharmony_ci	struct drm_framebuffer *old_fb = old_plane_state->fb;
30762306a36Sopenharmony_ci	const struct lsdc_cursor_plane_ops *ops = cursor->ops;
30862306a36Sopenharmony_ci	u64 addr = lsdc_fb_base_addr(new_fb);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (!new_plane_state->visible)
31162306a36Sopenharmony_ci		return;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	ops->update_position(cursor, new_plane_state->crtc_x, new_plane_state->crtc_y);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (!old_fb || old_fb != new_fb)
31662306a36Sopenharmony_ci		ops->update_bo_addr(cursor, addr);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	ops->update_cfg(cursor, CURSOR_SIZE_32X32, CURSOR_FORMAT_ARGB8888);
31962306a36Sopenharmony_ci}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic void ls7a1000_cursor_plane_atomic_disable(struct drm_plane *plane,
32262306a36Sopenharmony_ci						 struct drm_atomic_state *state)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	struct lsdc_cursor *cursor = to_lsdc_cursor(plane);
32562306a36Sopenharmony_ci	const struct lsdc_cursor_plane_ops *ops = cursor->ops;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	ops->update_cfg(cursor, CURSOR_SIZE_32X32, CURSOR_FORMAT_DISABLE);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs ls7a1000_cursor_plane_helper_funcs = {
33162306a36Sopenharmony_ci	.prepare_fb = lsdc_plane_prepare_fb,
33262306a36Sopenharmony_ci	.cleanup_fb = lsdc_plane_cleanup_fb,
33362306a36Sopenharmony_ci	.atomic_check = ls7a1000_cursor_plane_atomic_check,
33462306a36Sopenharmony_ci	.atomic_update = ls7a1000_cursor_plane_atomic_update,
33562306a36Sopenharmony_ci	.atomic_disable = ls7a1000_cursor_plane_atomic_disable,
33662306a36Sopenharmony_ci	.atomic_async_check = lsdc_cursor_plane_atomic_async_check,
33762306a36Sopenharmony_ci	.atomic_async_update = lsdc_cursor_plane_atomic_async_update,
33862306a36Sopenharmony_ci};
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci/* ls7a2000 cursor plane helpers */
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic int ls7a2000_cursor_plane_atomic_check(struct drm_plane *plane,
34362306a36Sopenharmony_ci					      struct drm_atomic_state *state)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state;
34662306a36Sopenharmony_ci	struct drm_crtc_state *new_crtc_state;
34762306a36Sopenharmony_ci	struct drm_crtc *crtc;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	new_plane_state = drm_atomic_get_new_plane_state(state, plane);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	crtc = new_plane_state->crtc;
35262306a36Sopenharmony_ci	if (!crtc) {
35362306a36Sopenharmony_ci		drm_dbg(plane->dev, "%s is not bind to a crtc\n", plane->name);
35462306a36Sopenharmony_ci		return 0;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (new_plane_state->crtc_w != new_plane_state->crtc_h) {
35862306a36Sopenharmony_ci		drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n",
35962306a36Sopenharmony_ci			new_plane_state->crtc_w, new_plane_state->crtc_h);
36062306a36Sopenharmony_ci		return -EINVAL;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	if (new_plane_state->crtc_w != 64 && new_plane_state->crtc_w != 32) {
36462306a36Sopenharmony_ci		drm_dbg(plane->dev, "unsupported cursor size: %ux%u\n",
36562306a36Sopenharmony_ci			new_plane_state->crtc_w, new_plane_state->crtc_h);
36662306a36Sopenharmony_ci		return -EINVAL;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	return drm_atomic_helper_check_plane_state(new_plane_state,
37262306a36Sopenharmony_ci						   new_crtc_state,
37362306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
37462306a36Sopenharmony_ci						   DRM_PLANE_NO_SCALING,
37562306a36Sopenharmony_ci						   true, true);
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci/* Update the format, size and location of the cursor */
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic void ls7a2000_cursor_plane_atomic_update(struct drm_plane *plane,
38162306a36Sopenharmony_ci						struct drm_atomic_state *state)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct lsdc_cursor *cursor = to_lsdc_cursor(plane);
38462306a36Sopenharmony_ci	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
38562306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
38662306a36Sopenharmony_ci	struct drm_framebuffer *new_fb = new_plane_state->fb;
38762306a36Sopenharmony_ci	struct drm_framebuffer *old_fb = old_plane_state->fb;
38862306a36Sopenharmony_ci	const struct lsdc_cursor_plane_ops *ops = cursor->ops;
38962306a36Sopenharmony_ci	enum lsdc_cursor_size cursor_size;
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	if (!new_plane_state->visible)
39262306a36Sopenharmony_ci		return;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	ops->update_position(cursor, new_plane_state->crtc_x, new_plane_state->crtc_y);
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (!old_fb || new_fb != old_fb) {
39762306a36Sopenharmony_ci		u64 addr = lsdc_fb_base_addr(new_fb);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci		ops->update_bo_addr(cursor, addr);
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	switch (new_plane_state->crtc_w) {
40362306a36Sopenharmony_ci	case 64:
40462306a36Sopenharmony_ci		cursor_size = CURSOR_SIZE_64X64;
40562306a36Sopenharmony_ci		break;
40662306a36Sopenharmony_ci	case 32:
40762306a36Sopenharmony_ci		cursor_size = CURSOR_SIZE_32X32;
40862306a36Sopenharmony_ci		break;
40962306a36Sopenharmony_ci	default:
41062306a36Sopenharmony_ci		cursor_size = CURSOR_SIZE_64X64;
41162306a36Sopenharmony_ci		break;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	ops->update_cfg(cursor, cursor_size, CURSOR_FORMAT_ARGB8888);
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void ls7a2000_cursor_plane_atomic_disable(struct drm_plane *plane,
41862306a36Sopenharmony_ci						 struct drm_atomic_state *state)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct lsdc_cursor *cursor = to_lsdc_cursor(plane);
42162306a36Sopenharmony_ci	const struct lsdc_cursor_plane_ops *hw_ops = cursor->ops;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	hw_ops->update_cfg(cursor, CURSOR_SIZE_64X64, CURSOR_FORMAT_DISABLE);
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs ls7a2000_cursor_plane_helper_funcs = {
42762306a36Sopenharmony_ci	.prepare_fb = lsdc_plane_prepare_fb,
42862306a36Sopenharmony_ci	.cleanup_fb = lsdc_plane_cleanup_fb,
42962306a36Sopenharmony_ci	.atomic_check = ls7a2000_cursor_plane_atomic_check,
43062306a36Sopenharmony_ci	.atomic_update = ls7a2000_cursor_plane_atomic_update,
43162306a36Sopenharmony_ci	.atomic_disable = ls7a2000_cursor_plane_atomic_disable,
43262306a36Sopenharmony_ci	.atomic_async_check = lsdc_cursor_plane_atomic_async_check,
43362306a36Sopenharmony_ci	.atomic_async_update = lsdc_cursor_plane_atomic_async_update,
43462306a36Sopenharmony_ci};
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic void lsdc_plane_atomic_print_state(struct drm_printer *p,
43762306a36Sopenharmony_ci					  const struct drm_plane_state *state)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct drm_framebuffer *fb = state->fb;
44062306a36Sopenharmony_ci	u64 addr;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (!fb)
44362306a36Sopenharmony_ci		return;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	addr = lsdc_fb_base_addr(fb);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	drm_printf(p, "\tdma addr=%llx\n", addr);
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic const struct drm_plane_funcs lsdc_plane_funcs = {
45162306a36Sopenharmony_ci	.update_plane = drm_atomic_helper_update_plane,
45262306a36Sopenharmony_ci	.disable_plane = drm_atomic_helper_disable_plane,
45362306a36Sopenharmony_ci	.destroy = drm_plane_cleanup,
45462306a36Sopenharmony_ci	.reset = drm_atomic_helper_plane_reset,
45562306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
45662306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
45762306a36Sopenharmony_ci	.atomic_print_state = lsdc_plane_atomic_print_state,
45862306a36Sopenharmony_ci};
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci/* Primary plane 0 hardware related ops  */
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic void lsdc_primary0_update_fb_addr(struct lsdc_primary *primary, u64 addr)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct lsdc_device *ldev = primary->ldev;
46562306a36Sopenharmony_ci	u32 status;
46662306a36Sopenharmony_ci	u32 lo, hi;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	/* 40-bit width physical address bus */
46962306a36Sopenharmony_ci	lo = addr & 0xFFFFFFFF;
47062306a36Sopenharmony_ci	hi = (addr >> 32) & 0xFF;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	status = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG);
47362306a36Sopenharmony_ci	if (status & FB_REG_IN_USING) {
47462306a36Sopenharmony_ci		lsdc_wreg32(ldev, LSDC_CRTC0_FB1_ADDR_LO_REG, lo);
47562306a36Sopenharmony_ci		lsdc_wreg32(ldev, LSDC_CRTC0_FB1_ADDR_HI_REG, hi);
47662306a36Sopenharmony_ci	} else {
47762306a36Sopenharmony_ci		lsdc_wreg32(ldev, LSDC_CRTC0_FB0_ADDR_LO_REG, lo);
47862306a36Sopenharmony_ci		lsdc_wreg32(ldev, LSDC_CRTC0_FB0_ADDR_HI_REG, hi);
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic void lsdc_primary0_update_fb_stride(struct lsdc_primary *primary, u32 stride)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	struct lsdc_device *ldev = primary->ldev;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC0_STRIDE_REG, stride);
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic void lsdc_primary0_update_fb_format(struct lsdc_primary *primary,
49062306a36Sopenharmony_ci					   const struct drm_format_info *format)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct lsdc_device *ldev = primary->ldev;
49362306a36Sopenharmony_ci	u32 status;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	status = lsdc_rreg32(ldev, LSDC_CRTC0_CFG_REG);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/*
49862306a36Sopenharmony_ci	 * TODO: add RGB565 support, only support XRBG8888 at present
49962306a36Sopenharmony_ci	 */
50062306a36Sopenharmony_ci	status &= ~CFG_PIX_FMT_MASK;
50162306a36Sopenharmony_ci	status |= LSDC_PF_XRGB8888;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC0_CFG_REG, status);
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci/* Primary plane 1 hardware related ops */
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cistatic void lsdc_primary1_update_fb_addr(struct lsdc_primary *primary, u64 addr)
50962306a36Sopenharmony_ci{
51062306a36Sopenharmony_ci	struct lsdc_device *ldev = primary->ldev;
51162306a36Sopenharmony_ci	u32 status;
51262306a36Sopenharmony_ci	u32 lo, hi;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	/* 40-bit width physical address bus */
51562306a36Sopenharmony_ci	lo = addr & 0xFFFFFFFF;
51662306a36Sopenharmony_ci	hi = (addr >> 32) & 0xFF;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	status = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG);
51962306a36Sopenharmony_ci	if (status & FB_REG_IN_USING) {
52062306a36Sopenharmony_ci		lsdc_wreg32(ldev, LSDC_CRTC1_FB1_ADDR_LO_REG, lo);
52162306a36Sopenharmony_ci		lsdc_wreg32(ldev, LSDC_CRTC1_FB1_ADDR_HI_REG, hi);
52262306a36Sopenharmony_ci	} else {
52362306a36Sopenharmony_ci		lsdc_wreg32(ldev, LSDC_CRTC1_FB0_ADDR_LO_REG, lo);
52462306a36Sopenharmony_ci		lsdc_wreg32(ldev, LSDC_CRTC1_FB0_ADDR_HI_REG, hi);
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cistatic void lsdc_primary1_update_fb_stride(struct lsdc_primary *primary, u32 stride)
52962306a36Sopenharmony_ci{
53062306a36Sopenharmony_ci	struct lsdc_device *ldev = primary->ldev;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC1_STRIDE_REG, stride);
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic void lsdc_primary1_update_fb_format(struct lsdc_primary *primary,
53662306a36Sopenharmony_ci					   const struct drm_format_info *format)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct lsdc_device *ldev = primary->ldev;
53962306a36Sopenharmony_ci	u32 status;
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	status = lsdc_rreg32(ldev, LSDC_CRTC1_CFG_REG);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	/*
54462306a36Sopenharmony_ci	 * TODO: add RGB565 support, only support XRBG8888 at present
54562306a36Sopenharmony_ci	 */
54662306a36Sopenharmony_ci	status &= ~CFG_PIX_FMT_MASK;
54762306a36Sopenharmony_ci	status |= LSDC_PF_XRGB8888;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC1_CFG_REG, status);
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic const struct lsdc_primary_plane_ops lsdc_primary_plane_hw_ops[2] = {
55362306a36Sopenharmony_ci	{
55462306a36Sopenharmony_ci		.update_fb_addr = lsdc_primary0_update_fb_addr,
55562306a36Sopenharmony_ci		.update_fb_stride = lsdc_primary0_update_fb_stride,
55662306a36Sopenharmony_ci		.update_fb_format = lsdc_primary0_update_fb_format,
55762306a36Sopenharmony_ci	},
55862306a36Sopenharmony_ci	{
55962306a36Sopenharmony_ci		.update_fb_addr = lsdc_primary1_update_fb_addr,
56062306a36Sopenharmony_ci		.update_fb_stride = lsdc_primary1_update_fb_stride,
56162306a36Sopenharmony_ci		.update_fb_format = lsdc_primary1_update_fb_format,
56262306a36Sopenharmony_ci	},
56362306a36Sopenharmony_ci};
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci/*
56662306a36Sopenharmony_ci * Update location, format, enable and disable state of the cursor,
56762306a36Sopenharmony_ci * For those who have two hardware cursor, let cursor 0 is attach to CRTC-0,
56862306a36Sopenharmony_ci * cursor 1 is attach to CRTC-1. Compositing the primary plane and cursor
56962306a36Sopenharmony_ci * plane is automatically done by hardware, the cursor is alway on the top of
57062306a36Sopenharmony_ci * the primary plane. In other word, z-order is fixed in hardware and cannot
57162306a36Sopenharmony_ci * be changed. For those old DC who has only one hardware cursor, we made it
57262306a36Sopenharmony_ci * shared by the two screen, this works on extend screen mode.
57362306a36Sopenharmony_ci */
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci/* cursor plane 0 (for pipe 0) related hardware ops */
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic void lsdc_cursor0_update_bo_addr(struct lsdc_cursor *cursor, u64 addr)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct lsdc_device *ldev = cursor->ldev;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	/* 40-bit width physical address bus */
58262306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_HI_REG, (addr >> 32) & 0xFF);
58362306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_LO_REG, addr);
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic void lsdc_cursor0_update_position(struct lsdc_cursor *cursor, int x, int y)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	struct lsdc_device *ldev = cursor->ldev;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (x < 0)
59162306a36Sopenharmony_ci		x = 0;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	if (y < 0)
59462306a36Sopenharmony_ci		y = 0;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CURSOR0_POSITION_REG, (y << 16) | x);
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic void lsdc_cursor0_update_cfg(struct lsdc_cursor *cursor,
60062306a36Sopenharmony_ci				    enum lsdc_cursor_size cursor_size,
60162306a36Sopenharmony_ci				    enum lsdc_cursor_format fmt)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	struct lsdc_device *ldev = cursor->ldev;
60462306a36Sopenharmony_ci	u32 cfg;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	cfg = CURSOR_ON_CRTC0 << CURSOR_LOCATION_SHIFT |
60762306a36Sopenharmony_ci	      cursor_size << CURSOR_SIZE_SHIFT |
60862306a36Sopenharmony_ci	      fmt << CURSOR_FORMAT_SHIFT;
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CURSOR0_CFG_REG, cfg);
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci/* cursor plane 1 (for pipe 1) related hardware ops */
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_cistatic void lsdc_cursor1_update_bo_addr(struct lsdc_cursor *cursor, u64 addr)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct lsdc_device *ldev = cursor->ldev;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	/* 40-bit width physical address bus */
62062306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CURSOR1_ADDR_HI_REG, (addr >> 32) & 0xFF);
62162306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CURSOR1_ADDR_LO_REG, addr);
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic void lsdc_cursor1_update_position(struct lsdc_cursor *cursor, int x, int y)
62562306a36Sopenharmony_ci{
62662306a36Sopenharmony_ci	struct lsdc_device *ldev = cursor->ldev;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (x < 0)
62962306a36Sopenharmony_ci		x = 0;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (y < 0)
63262306a36Sopenharmony_ci		y = 0;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CURSOR1_POSITION_REG, (y << 16) | x);
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic void lsdc_cursor1_update_cfg(struct lsdc_cursor *cursor,
63862306a36Sopenharmony_ci				    enum lsdc_cursor_size cursor_size,
63962306a36Sopenharmony_ci				    enum lsdc_cursor_format fmt)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct lsdc_device *ldev = cursor->ldev;
64262306a36Sopenharmony_ci	u32 cfg;
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ci	cfg = CURSOR_ON_CRTC1 << CURSOR_LOCATION_SHIFT |
64562306a36Sopenharmony_ci	      cursor_size << CURSOR_SIZE_SHIFT |
64662306a36Sopenharmony_ci	      fmt << CURSOR_FORMAT_SHIFT;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CURSOR1_CFG_REG, cfg);
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci/* The hardware cursors become normal since ls7a2000/ls2k2000 */
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_cistatic const struct lsdc_cursor_plane_ops ls7a2000_cursor_hw_ops[2] = {
65462306a36Sopenharmony_ci	{
65562306a36Sopenharmony_ci		.update_bo_addr = lsdc_cursor0_update_bo_addr,
65662306a36Sopenharmony_ci		.update_cfg = lsdc_cursor0_update_cfg,
65762306a36Sopenharmony_ci		.update_position = lsdc_cursor0_update_position,
65862306a36Sopenharmony_ci	},
65962306a36Sopenharmony_ci	{
66062306a36Sopenharmony_ci		.update_bo_addr = lsdc_cursor1_update_bo_addr,
66162306a36Sopenharmony_ci		.update_cfg = lsdc_cursor1_update_cfg,
66262306a36Sopenharmony_ci		.update_position = lsdc_cursor1_update_position,
66362306a36Sopenharmony_ci	},
66462306a36Sopenharmony_ci};
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci/* Quirks for cursor 1, only for old loongson display controller */
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic void lsdc_cursor1_update_bo_addr_quirk(struct lsdc_cursor *cursor, u64 addr)
66962306a36Sopenharmony_ci{
67062306a36Sopenharmony_ci	struct lsdc_device *ldev = cursor->ldev;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	/* 40-bit width physical address bus */
67362306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_HI_REG, (addr >> 32) & 0xFF);
67462306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CURSOR0_ADDR_LO_REG, addr);
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic void lsdc_cursor1_update_position_quirk(struct lsdc_cursor *cursor, int x, int y)
67862306a36Sopenharmony_ci{
67962306a36Sopenharmony_ci	struct lsdc_device *ldev = cursor->ldev;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (x < 0)
68262306a36Sopenharmony_ci		x = 0;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (y < 0)
68562306a36Sopenharmony_ci		y = 0;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CURSOR0_POSITION_REG, (y << 16) | x);
68862306a36Sopenharmony_ci}
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_cistatic void lsdc_cursor1_update_cfg_quirk(struct lsdc_cursor *cursor,
69162306a36Sopenharmony_ci					  enum lsdc_cursor_size cursor_size,
69262306a36Sopenharmony_ci					  enum lsdc_cursor_format fmt)
69362306a36Sopenharmony_ci{
69462306a36Sopenharmony_ci	struct lsdc_device *ldev = cursor->ldev;
69562306a36Sopenharmony_ci	u32 cfg;
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	cfg = CURSOR_ON_CRTC1 << CURSOR_LOCATION_SHIFT |
69862306a36Sopenharmony_ci	      cursor_size << CURSOR_SIZE_SHIFT |
69962306a36Sopenharmony_ci	      fmt << CURSOR_FORMAT_SHIFT;
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CURSOR0_CFG_REG, cfg);
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci/*
70562306a36Sopenharmony_ci * The unforgiving LS7A1000/LS2K1000 has only one hardware cursors plane
70662306a36Sopenharmony_ci */
70762306a36Sopenharmony_cistatic const struct lsdc_cursor_plane_ops ls7a1000_cursor_hw_ops[2] = {
70862306a36Sopenharmony_ci	{
70962306a36Sopenharmony_ci		.update_bo_addr = lsdc_cursor0_update_bo_addr,
71062306a36Sopenharmony_ci		.update_cfg = lsdc_cursor0_update_cfg,
71162306a36Sopenharmony_ci		.update_position = lsdc_cursor0_update_position,
71262306a36Sopenharmony_ci	},
71362306a36Sopenharmony_ci	{
71462306a36Sopenharmony_ci		.update_bo_addr = lsdc_cursor1_update_bo_addr_quirk,
71562306a36Sopenharmony_ci		.update_cfg = lsdc_cursor1_update_cfg_quirk,
71662306a36Sopenharmony_ci		.update_position = lsdc_cursor1_update_position_quirk,
71762306a36Sopenharmony_ci	},
71862306a36Sopenharmony_ci};
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ciint lsdc_primary_plane_init(struct drm_device *ddev,
72162306a36Sopenharmony_ci			    struct drm_plane *plane,
72262306a36Sopenharmony_ci			    unsigned int index)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct lsdc_primary *primary = to_lsdc_primary(plane);
72562306a36Sopenharmony_ci	int ret;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	ret = drm_universal_plane_init(ddev, plane, 1 << index,
72862306a36Sopenharmony_ci				       &lsdc_plane_funcs,
72962306a36Sopenharmony_ci				       lsdc_primary_formats,
73062306a36Sopenharmony_ci				       ARRAY_SIZE(lsdc_primary_formats),
73162306a36Sopenharmony_ci				       lsdc_fb_format_modifiers,
73262306a36Sopenharmony_ci				       DRM_PLANE_TYPE_PRIMARY,
73362306a36Sopenharmony_ci				       "ls-primary-plane-%u", index);
73462306a36Sopenharmony_ci	if (ret)
73562306a36Sopenharmony_ci		return ret;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	drm_plane_helper_add(plane, &lsdc_primary_helper_funcs);
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci	primary->ldev = to_lsdc(ddev);
74062306a36Sopenharmony_ci	primary->ops = &lsdc_primary_plane_hw_ops[index];
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	return 0;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ciint ls7a1000_cursor_plane_init(struct drm_device *ddev,
74662306a36Sopenharmony_ci			       struct drm_plane *plane,
74762306a36Sopenharmony_ci			       unsigned int index)
74862306a36Sopenharmony_ci{
74962306a36Sopenharmony_ci	struct lsdc_cursor *cursor = to_lsdc_cursor(plane);
75062306a36Sopenharmony_ci	int ret;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	ret = drm_universal_plane_init(ddev, plane, 1 << index,
75362306a36Sopenharmony_ci				       &lsdc_plane_funcs,
75462306a36Sopenharmony_ci				       lsdc_cursor_formats,
75562306a36Sopenharmony_ci				       ARRAY_SIZE(lsdc_cursor_formats),
75662306a36Sopenharmony_ci				       lsdc_fb_format_modifiers,
75762306a36Sopenharmony_ci				       DRM_PLANE_TYPE_CURSOR,
75862306a36Sopenharmony_ci				       "ls-cursor-plane-%u", index);
75962306a36Sopenharmony_ci	if (ret)
76062306a36Sopenharmony_ci		return ret;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	cursor->ldev = to_lsdc(ddev);
76362306a36Sopenharmony_ci	cursor->ops = &ls7a1000_cursor_hw_ops[index];
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	drm_plane_helper_add(plane, &ls7a1000_cursor_plane_helper_funcs);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	return 0;
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ciint ls7a2000_cursor_plane_init(struct drm_device *ddev,
77162306a36Sopenharmony_ci			       struct drm_plane *plane,
77262306a36Sopenharmony_ci			       unsigned int index)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct lsdc_cursor *cursor = to_lsdc_cursor(plane);
77562306a36Sopenharmony_ci	int ret;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	ret = drm_universal_plane_init(ddev, plane, 1 << index,
77862306a36Sopenharmony_ci				       &lsdc_plane_funcs,
77962306a36Sopenharmony_ci				       lsdc_cursor_formats,
78062306a36Sopenharmony_ci				       ARRAY_SIZE(lsdc_cursor_formats),
78162306a36Sopenharmony_ci				       lsdc_fb_format_modifiers,
78262306a36Sopenharmony_ci				       DRM_PLANE_TYPE_CURSOR,
78362306a36Sopenharmony_ci				       "ls-cursor-plane-%u", index);
78462306a36Sopenharmony_ci	if (ret)
78562306a36Sopenharmony_ci		return ret;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	cursor->ldev = to_lsdc(ddev);
78862306a36Sopenharmony_ci	cursor->ops = &ls7a2000_cursor_hw_ops[index];
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	drm_plane_helper_add(plane, &ls7a2000_cursor_plane_helper_funcs);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	return 0;
79362306a36Sopenharmony_ci}
794