1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2012 Russell King
4  *  Rewritten from the dovefb driver, and Armada510 manuals.
5  */
6 #include <drm/drmP.h>
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_atomic_helper.h>
9 #include <drm/drm_plane_helper.h>
10 #include "armada_crtc.h"
11 #include "armada_drm.h"
12 #include "armada_fb.h"
13 #include "armada_gem.h"
14 #include "armada_hw.h"
15 #include "armada_plane.h"
16 #include "armada_trace.h"
17 
18 static const uint32_t armada_primary_formats[] = {
19 	DRM_FORMAT_UYVY,
20 	DRM_FORMAT_YUYV,
21 	DRM_FORMAT_VYUY,
22 	DRM_FORMAT_YVYU,
23 	DRM_FORMAT_ARGB8888,
24 	DRM_FORMAT_ABGR8888,
25 	DRM_FORMAT_XRGB8888,
26 	DRM_FORMAT_XBGR8888,
27 	DRM_FORMAT_RGB888,
28 	DRM_FORMAT_BGR888,
29 	DRM_FORMAT_ARGB1555,
30 	DRM_FORMAT_ABGR1555,
31 	DRM_FORMAT_RGB565,
32 	DRM_FORMAT_BGR565,
33 };
34 
35 void armada_drm_plane_calc(struct drm_plane_state *state, u32 addrs[2][3],
36 	u16 pitches[3], bool interlaced)
37 {
38 	struct drm_framebuffer *fb = state->fb;
39 	const struct drm_format_info *format = fb->format;
40 	unsigned int num_planes = format->num_planes;
41 	unsigned int x = state->src.x1 >> 16;
42 	unsigned int y = state->src.y1 >> 16;
43 	u32 addr = drm_fb_obj(fb)->dev_addr;
44 	int i;
45 
46 	DRM_DEBUG_KMS("pitch %u x %d y %d bpp %d\n",
47 		      fb->pitches[0], x, y, format->cpp[0] * 8);
48 
49 	if (num_planes > 3)
50 		num_planes = 3;
51 
52 	addrs[0][0] = addr + fb->offsets[0] + y * fb->pitches[0] +
53 		      x * format->cpp[0];
54 	pitches[0] = fb->pitches[0];
55 
56 	y /= format->vsub;
57 	x /= format->hsub;
58 
59 	for (i = 1; i < num_planes; i++) {
60 		addrs[0][i] = addr + fb->offsets[i] + y * fb->pitches[i] +
61 			      x * format->cpp[i];
62 		pitches[i] = fb->pitches[i];
63 	}
64 	for (; i < 3; i++) {
65 		addrs[0][i] = 0;
66 		pitches[i] = 0;
67 	}
68 	if (interlaced) {
69 		for (i = 0; i < 3; i++) {
70 			addrs[1][i] = addrs[0][i] + pitches[i];
71 			pitches[i] *= 2;
72 		}
73 	} else {
74 		for (i = 0; i < 3; i++)
75 			addrs[1][i] = addrs[0][i];
76 	}
77 }
78 
79 int armada_drm_plane_prepare_fb(struct drm_plane *plane,
80 	struct drm_plane_state *state)
81 {
82 	DRM_DEBUG_KMS("[PLANE:%d:%s] [FB:%d]\n",
83 		plane->base.id, plane->name,
84 		state->fb ? state->fb->base.id : 0);
85 
86 	/*
87 	 * Take a reference on the new framebuffer - we want to
88 	 * hold on to it while the hardware is displaying it.
89 	 */
90 	if (state->fb)
91 		drm_framebuffer_get(state->fb);
92 	return 0;
93 }
94 
95 void armada_drm_plane_cleanup_fb(struct drm_plane *plane,
96 	struct drm_plane_state *old_state)
97 {
98 	DRM_DEBUG_KMS("[PLANE:%d:%s] [FB:%d]\n",
99 		plane->base.id, plane->name,
100 		old_state->fb ? old_state->fb->base.id : 0);
101 
102 	if (old_state->fb)
103 		drm_framebuffer_put(old_state->fb);
104 }
105 
106 int armada_drm_plane_atomic_check(struct drm_plane *plane,
107 	struct drm_plane_state *state)
108 {
109 	struct armada_plane_state *st = to_armada_plane_state(state);
110 	struct drm_crtc *crtc = state->crtc;
111 	struct drm_crtc_state *crtc_state;
112 	bool interlace;
113 	int ret;
114 
115 	if (!state->fb || WARN_ON(!state->crtc)) {
116 		state->visible = false;
117 		return 0;
118 	}
119 
120 	if (state->state)
121 		crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
122 	else
123 		crtc_state = crtc->state;
124 
125 	ret = drm_atomic_helper_check_plane_state(state, crtc_state, 0,
126 						  INT_MAX, true, false);
127 	if (ret)
128 		return ret;
129 
130 	interlace = crtc_state->adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE;
131 	if (interlace) {
132 		if ((state->dst.y1 | state->dst.y2) & 1)
133 			return -EINVAL;
134 		st->src_hw = drm_rect_height(&state->src) >> 17;
135 		st->dst_yx = state->dst.y1 >> 1;
136 		st->dst_hw = drm_rect_height(&state->dst) >> 1;
137 	} else {
138 		st->src_hw = drm_rect_height(&state->src) >> 16;
139 		st->dst_yx = state->dst.y1;
140 		st->dst_hw = drm_rect_height(&state->dst);
141 	}
142 
143 	st->src_hw <<= 16;
144 	st->src_hw |= drm_rect_width(&state->src) >> 16;
145 	st->dst_yx <<= 16;
146 	st->dst_yx |= state->dst.x1 & 0x0000ffff;
147 	st->dst_hw <<= 16;
148 	st->dst_hw |= drm_rect_width(&state->dst) & 0x0000ffff;
149 
150 	armada_drm_plane_calc(state, st->addrs, st->pitches, interlace);
151 	st->interlace = interlace;
152 
153 	return 0;
154 }
155 
156 static void armada_drm_primary_plane_atomic_update(struct drm_plane *plane,
157 	struct drm_plane_state *old_state)
158 {
159 	struct drm_plane_state *state = plane->state;
160 	struct armada_crtc *dcrtc;
161 	struct armada_regs *regs;
162 	u32 cfg, cfg_mask, val;
163 	unsigned int idx;
164 
165 	DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name);
166 
167 	if (!state->fb || WARN_ON(!state->crtc))
168 		return;
169 
170 	DRM_DEBUG_KMS("[PLANE:%d:%s] is on [CRTC:%d:%s] with [FB:%d] visible %u->%u\n",
171 		plane->base.id, plane->name,
172 		state->crtc->base.id, state->crtc->name,
173 		state->fb->base.id,
174 		old_state->visible, state->visible);
175 
176 	dcrtc = drm_to_armada_crtc(state->crtc);
177 	regs = dcrtc->regs + dcrtc->regs_idx;
178 
179 	idx = 0;
180 	if (!old_state->visible && state->visible) {
181 		val = CFG_PDWN64x66;
182 		if (drm_fb_to_armada_fb(state->fb)->fmt > CFG_420)
183 			val |= CFG_PDWN256x24;
184 		armada_reg_queue_mod(regs, idx, 0, val, LCD_SPU_SRAM_PARA1);
185 	}
186 	val = armada_src_hw(state);
187 	if (armada_src_hw(old_state) != val)
188 		armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_HPXL_VLN);
189 	val = armada_dst_yx(state);
190 	if (armada_dst_yx(old_state) != val)
191 		armada_reg_queue_set(regs, idx, val, LCD_SPU_GRA_OVSA_HPXL_VLN);
192 	val = armada_dst_hw(state);
193 	if (armada_dst_hw(old_state) != val)
194 		armada_reg_queue_set(regs, idx, val, LCD_SPU_GZM_HPXL_VLN);
195 	if (old_state->src.x1 != state->src.x1 ||
196 	    old_state->src.y1 != state->src.y1 ||
197 	    old_state->fb != state->fb ||
198 	    state->crtc->state->mode_changed) {
199 		armada_reg_queue_set(regs, idx, armada_addr(state, 0, 0),
200 				     LCD_CFG_GRA_START_ADDR0);
201 		armada_reg_queue_set(regs, idx, armada_addr(state, 1, 0),
202 				     LCD_CFG_GRA_START_ADDR1);
203 		armada_reg_queue_mod(regs, idx, armada_pitch(state, 0), 0xffff,
204 				     LCD_CFG_GRA_PITCH);
205 	}
206 	if (old_state->fb != state->fb ||
207 	    state->crtc->state->mode_changed) {
208 		cfg = CFG_GRA_FMT(drm_fb_to_armada_fb(state->fb)->fmt) |
209 		      CFG_GRA_MOD(drm_fb_to_armada_fb(state->fb)->mod);
210 		if (drm_fb_to_armada_fb(state->fb)->fmt > CFG_420)
211 			cfg |= CFG_PALETTE_ENA;
212 		if (state->visible)
213 			cfg |= CFG_GRA_ENA;
214 		if (to_armada_plane_state(state)->interlace)
215 			cfg |= CFG_GRA_FTOGGLE;
216 		cfg_mask = CFG_GRAFORMAT |
217 			   CFG_GRA_MOD(CFG_SWAPRB | CFG_SWAPUV |
218 				       CFG_SWAPYU | CFG_YUV2RGB) |
219 			   CFG_PALETTE_ENA | CFG_GRA_FTOGGLE |
220 			   CFG_GRA_ENA;
221 	} else if (old_state->visible != state->visible) {
222 		cfg = state->visible ? CFG_GRA_ENA : 0;
223 		cfg_mask = CFG_GRA_ENA;
224 	} else {
225 		cfg = cfg_mask = 0;
226 	}
227 	if (drm_rect_width(&old_state->src) != drm_rect_width(&state->src) ||
228 	    drm_rect_width(&old_state->dst) != drm_rect_width(&state->dst)) {
229 		cfg_mask |= CFG_GRA_HSMOOTH;
230 		if (drm_rect_width(&state->src) >> 16 !=
231 		    drm_rect_width(&state->dst))
232 			cfg |= CFG_GRA_HSMOOTH;
233 	}
234 
235 	if (cfg_mask)
236 		armada_reg_queue_mod(regs, idx, cfg, cfg_mask,
237 				     LCD_SPU_DMA_CTRL0);
238 
239 	dcrtc->regs_idx += idx;
240 }
241 
242 static void armada_drm_primary_plane_atomic_disable(struct drm_plane *plane,
243 	struct drm_plane_state *old_state)
244 {
245 	struct armada_crtc *dcrtc;
246 	struct armada_regs *regs;
247 	unsigned int idx = 0;
248 
249 	DRM_DEBUG_KMS("[PLANE:%d:%s]\n", plane->base.id, plane->name);
250 
251 	if (!old_state->crtc)
252 		return;
253 
254 	DRM_DEBUG_KMS("[PLANE:%d:%s] was on [CRTC:%d:%s] with [FB:%d]\n",
255 		plane->base.id, plane->name,
256 		old_state->crtc->base.id, old_state->crtc->name,
257 		old_state->fb->base.id);
258 
259 	dcrtc = drm_to_armada_crtc(old_state->crtc);
260 	regs = dcrtc->regs + dcrtc->regs_idx;
261 
262 	/* Disable plane and power down most RAMs and FIFOs */
263 	armada_reg_queue_mod(regs, idx, 0, CFG_GRA_ENA, LCD_SPU_DMA_CTRL0);
264 	armada_reg_queue_mod(regs, idx, CFG_PDWN256x32 | CFG_PDWN256x24 |
265 			     CFG_PDWN32x32 | CFG_PDWN64x66,
266 			     0, LCD_SPU_SRAM_PARA1);
267 
268 	dcrtc->regs_idx += idx;
269 }
270 
271 static const struct drm_plane_helper_funcs armada_primary_plane_helper_funcs = {
272 	.prepare_fb	= armada_drm_plane_prepare_fb,
273 	.cleanup_fb	= armada_drm_plane_cleanup_fb,
274 	.atomic_check	= armada_drm_plane_atomic_check,
275 	.atomic_update	= armada_drm_primary_plane_atomic_update,
276 	.atomic_disable	= armada_drm_primary_plane_atomic_disable,
277 };
278 
279 void armada_plane_reset(struct drm_plane *plane)
280 {
281 	struct armada_plane_state *st;
282 	if (plane->state)
283 		__drm_atomic_helper_plane_destroy_state(plane->state);
284 	kfree(plane->state);
285 	st = kzalloc(sizeof(*st), GFP_KERNEL);
286 	if (st)
287 		__drm_atomic_helper_plane_reset(plane, &st->base);
288 }
289 
290 struct drm_plane_state *armada_plane_duplicate_state(struct drm_plane *plane)
291 {
292 	struct armada_plane_state *st;
293 
294 	if (WARN_ON(!plane->state))
295 		return NULL;
296 
297 	st = kmemdup(plane->state, sizeof(*st), GFP_KERNEL);
298 	if (st)
299 		__drm_atomic_helper_plane_duplicate_state(plane, &st->base);
300 
301 	return &st->base;
302 }
303 
304 static const struct drm_plane_funcs armada_primary_plane_funcs = {
305 	.update_plane	= drm_atomic_helper_update_plane,
306 	.disable_plane	= drm_atomic_helper_disable_plane,
307 	.destroy	= drm_primary_helper_destroy,
308 	.reset		= armada_plane_reset,
309 	.atomic_duplicate_state = armada_plane_duplicate_state,
310 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
311 };
312 
313 int armada_drm_primary_plane_init(struct drm_device *drm,
314 	struct drm_plane *primary)
315 {
316 	int ret;
317 
318 	drm_plane_helper_add(primary, &armada_primary_plane_helper_funcs);
319 
320 	ret = drm_universal_plane_init(drm, primary, 0,
321 				       &armada_primary_plane_funcs,
322 				       armada_primary_formats,
323 				       ARRAY_SIZE(armada_primary_formats),
324 				       NULL,
325 				       DRM_PLANE_TYPE_PRIMARY, NULL);
326 
327 	return ret;
328 }
329