xref: /linux/drivers/gpu/drm/renesas/shmobile/shmob_drm_plane.c (revision ab1c247094e323177a578b38f0325bf79f0317ac)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * shmob_drm_plane.c  --  SH Mobile DRM Planes
4  *
5  * Copyright (C) 2012 Renesas Electronics Corporation
6  *
7  * Laurent Pinchart (laurent.pinchart@ideasonboard.com)
8  */
9 
10 #include <drm/drm_atomic.h>
11 #include <drm/drm_atomic_helper.h>
12 #include <drm/drm_crtc.h>
13 #include <drm/drm_fb_dma_helper.h>
14 #include <drm/drm_fourcc.h>
15 #include <drm/drm_framebuffer.h>
16 #include <drm/drm_gem_dma_helper.h>
17 #include <drm/drm_plane_helper.h>
18 
19 #include "shmob_drm_drv.h"
20 #include "shmob_drm_kms.h"
21 #include "shmob_drm_plane.h"
22 #include "shmob_drm_regs.h"
23 
24 struct shmob_drm_plane {
25 	struct drm_plane base;
26 	unsigned int index;
27 };
28 
29 struct shmob_drm_plane_state {
30 	struct drm_plane_state base;
31 
32 	const struct shmob_drm_format_info *format;
33 	u32 dma[2];
34 };
35 
36 static inline struct shmob_drm_plane *to_shmob_plane(struct drm_plane *plane)
37 {
38 	return container_of(plane, struct shmob_drm_plane, base);
39 }
40 
41 static inline struct shmob_drm_plane_state *to_shmob_plane_state(struct drm_plane_state *state)
42 {
43 	return container_of(state, struct shmob_drm_plane_state, base);
44 }
45 
46 static void shmob_drm_plane_compute_base(struct shmob_drm_plane_state *sstate)
47 {
48 	struct drm_framebuffer *fb = sstate->base.fb;
49 	unsigned int x = sstate->base.src_x >> 16;
50 	unsigned int y = sstate->base.src_y >> 16;
51 	struct drm_gem_dma_object *gem;
52 	unsigned int bpp;
53 
54 	bpp = shmob_drm_format_is_yuv(sstate->format) ? 8 : sstate->format->bpp;
55 	gem = drm_fb_dma_get_gem_obj(fb, 0);
56 	sstate->dma[0] = gem->dma_addr + fb->offsets[0]
57 		       + y * fb->pitches[0] + x * bpp / 8;
58 
59 	if (shmob_drm_format_is_yuv(sstate->format)) {
60 		bpp = sstate->format->bpp - 8;
61 		gem = drm_fb_dma_get_gem_obj(fb, 1);
62 		sstate->dma[1] = gem->dma_addr + fb->offsets[1]
63 			       + y / (bpp == 4 ? 2 : 1) * fb->pitches[1]
64 			       + x * (bpp == 16 ? 2 : 1);
65 	}
66 }
67 
68 static void shmob_drm_primary_plane_setup(struct shmob_drm_plane *splane,
69 					  struct drm_plane_state *state)
70 {
71 	struct shmob_drm_plane_state *sstate = to_shmob_plane_state(state);
72 	struct shmob_drm_device *sdev = to_shmob_device(splane->base.dev);
73 	struct drm_framebuffer *fb = state->fb;
74 
75 	/* TODO: Handle YUV colorspaces. Hardcode REC709 for now. */
76 	lcdc_write(sdev, LDDFR, sstate->format->lddfr | LDDFR_CF1);
77 	lcdc_write(sdev, LDMLSR, fb->pitches[0]);
78 
79 	/* Word and long word swap. */
80 	lcdc_write(sdev, LDDDSR, sstate->format->ldddsr);
81 
82 	lcdc_write_mirror(sdev, LDSA1R, sstate->dma[0]);
83 	if (shmob_drm_format_is_yuv(sstate->format))
84 		lcdc_write_mirror(sdev, LDSA2R, sstate->dma[1]);
85 
86 	lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
87 }
88 
89 static void shmob_drm_overlay_plane_setup(struct shmob_drm_plane *splane,
90 					  struct drm_plane_state *state)
91 {
92 	struct shmob_drm_plane_state *sstate = to_shmob_plane_state(state);
93 	struct shmob_drm_device *sdev = to_shmob_device(splane->base.dev);
94 	struct drm_framebuffer *fb = state->fb;
95 	u32 format;
96 
97 	/* TODO: Support ROP3 mode */
98 	format = LDBBSIFR_EN | ((state->alpha >> 8) << LDBBSIFR_LAY_SHIFT) |
99 		 sstate->format->ldbbsifr;
100 
101 #define plane_reg_dump(sdev, splane, reg) \
102 	dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x 0x%08x\n", __func__, \
103 		splane->index, #reg, \
104 		lcdc_read(sdev, reg(splane->index)), \
105 		lcdc_read(sdev, reg(splane->index) + LCDC_SIDE_B_OFFSET))
106 
107 	plane_reg_dump(sdev, splane, LDBnBSIFR);
108 	plane_reg_dump(sdev, splane, LDBnBSSZR);
109 	plane_reg_dump(sdev, splane, LDBnBLOCR);
110 	plane_reg_dump(sdev, splane, LDBnBSMWR);
111 	plane_reg_dump(sdev, splane, LDBnBSAYR);
112 	plane_reg_dump(sdev, splane, LDBnBSACR);
113 
114 	lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index));
115 	dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,
116 		"LDBCR", lcdc_read(sdev, LDBCR));
117 
118 	lcdc_write(sdev, LDBnBSIFR(splane->index), format);
119 
120 	lcdc_write(sdev, LDBnBSSZR(splane->index),
121 		   (state->crtc_h << LDBBSSZR_BVSS_SHIFT) |
122 		   (state->crtc_w << LDBBSSZR_BHSS_SHIFT));
123 	lcdc_write(sdev, LDBnBLOCR(splane->index),
124 		   (state->crtc_y << LDBBLOCR_CVLC_SHIFT) |
125 		   (state->crtc_x << LDBBLOCR_CHLC_SHIFT));
126 	lcdc_write(sdev, LDBnBSMWR(splane->index),
127 		   fb->pitches[0] << LDBBSMWR_BSMW_SHIFT);
128 
129 	lcdc_write(sdev, LDBnBSAYR(splane->index), sstate->dma[0]);
130 	if (shmob_drm_format_is_yuv(sstate->format))
131 		lcdc_write(sdev, LDBnBSACR(splane->index), sstate->dma[1]);
132 
133 	lcdc_write(sdev, LDBCR,
134 		   LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index));
135 	dev_dbg(sdev->ddev.dev, "%s(%u): %s 0x%08x\n", __func__, splane->index,
136 		"LDBCR", lcdc_read(sdev, LDBCR));
137 
138 	plane_reg_dump(sdev, splane, LDBnBSIFR);
139 	plane_reg_dump(sdev, splane, LDBnBSSZR);
140 	plane_reg_dump(sdev, splane, LDBnBLOCR);
141 	plane_reg_dump(sdev, splane, LDBnBSMWR);
142 	plane_reg_dump(sdev, splane, LDBnBSAYR);
143 	plane_reg_dump(sdev, splane, LDBnBSACR);
144 }
145 
146 static int shmob_drm_plane_atomic_check(struct drm_plane *plane,
147 					struct drm_atomic_state *state)
148 {
149 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
150 	struct shmob_drm_plane_state *sstate = to_shmob_plane_state(new_plane_state);
151 	struct drm_crtc_state *crtc_state;
152 	bool is_primary = plane->type == DRM_PLANE_TYPE_PRIMARY;
153 	int ret;
154 
155 	if (!new_plane_state->crtc) {
156 		/*
157 		 * The visible field is not reset by the DRM core but only
158 		 * updated by drm_atomic_helper_check_plane_state(), set it
159 		 * manually.
160 		 */
161 		new_plane_state->visible = false;
162 		sstate->format = NULL;
163 		return 0;
164 	}
165 
166 	crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc);
167 	if (IS_ERR(crtc_state))
168 		return PTR_ERR(crtc_state);
169 
170 	ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
171 						  DRM_PLANE_NO_SCALING,
172 						  DRM_PLANE_NO_SCALING,
173 						  !is_primary, true);
174 	if (ret < 0)
175 		return ret;
176 
177 	if (!new_plane_state->visible) {
178 		sstate->format = NULL;
179 		return 0;
180 	}
181 
182 	sstate->format = shmob_drm_format_info(new_plane_state->fb->format->format);
183 	if (!sstate->format) {
184 		dev_dbg(plane->dev->dev,
185 			"plane_atomic_check: unsupported format %p4cc\n",
186 			&new_plane_state->fb->format->format);
187 		return -EINVAL;
188 	}
189 
190 	shmob_drm_plane_compute_base(sstate);
191 
192 	return 0;
193 }
194 
195 static void shmob_drm_plane_atomic_update(struct drm_plane *plane,
196 					  struct drm_atomic_state *state)
197 {
198 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
199 	struct shmob_drm_plane *splane = to_shmob_plane(plane);
200 
201 	if (!new_plane_state->visible)
202 		return;
203 
204 	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
205 		shmob_drm_primary_plane_setup(splane, new_plane_state);
206 	else
207 		shmob_drm_overlay_plane_setup(splane, new_plane_state);
208 }
209 
210 static void shmob_drm_plane_atomic_disable(struct drm_plane *plane,
211 					   struct drm_atomic_state *state)
212 {
213 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
214 	struct shmob_drm_device *sdev = to_shmob_device(plane->dev);
215 	struct shmob_drm_plane *splane = to_shmob_plane(plane);
216 
217 	if (!old_state->crtc)
218 		return;
219 
220 	if (plane->type != DRM_PLANE_TYPE_OVERLAY)
221 		return;
222 
223 	lcdc_write(sdev, LDBCR, LDBCR_UPC(splane->index));
224 	lcdc_write(sdev, LDBnBSIFR(splane->index), 0);
225 	lcdc_write(sdev, LDBCR,
226 			 LDBCR_UPF(splane->index) | LDBCR_UPD(splane->index));
227 }
228 
229 static struct drm_plane_state *
230 shmob_drm_plane_atomic_duplicate_state(struct drm_plane *plane)
231 {
232 	struct shmob_drm_plane_state *state;
233 	struct shmob_drm_plane_state *copy;
234 
235 	if (WARN_ON(!plane->state))
236 		return NULL;
237 
238 	state = to_shmob_plane_state(plane->state);
239 	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
240 	if (copy == NULL)
241 		return NULL;
242 
243 	__drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
244 
245 	return &copy->base;
246 }
247 
248 static void shmob_drm_plane_atomic_destroy_state(struct drm_plane *plane,
249 						 struct drm_plane_state *state)
250 {
251 	__drm_atomic_helper_plane_destroy_state(state);
252 	kfree(to_shmob_plane_state(state));
253 }
254 
255 static void shmob_drm_plane_reset(struct drm_plane *plane)
256 {
257 	struct shmob_drm_plane_state *state;
258 
259 	if (plane->state) {
260 		shmob_drm_plane_atomic_destroy_state(plane, plane->state);
261 		plane->state = NULL;
262 	}
263 
264 	state = kzalloc(sizeof(*state), GFP_KERNEL);
265 	if (state == NULL)
266 		return;
267 
268 	__drm_atomic_helper_plane_reset(plane, &state->base);
269 }
270 
271 static const struct drm_plane_helper_funcs shmob_drm_plane_helper_funcs = {
272 	.atomic_check = shmob_drm_plane_atomic_check,
273 	.atomic_update = shmob_drm_plane_atomic_update,
274 	.atomic_disable = shmob_drm_plane_atomic_disable,
275 };
276 
277 static const struct drm_plane_funcs shmob_drm_plane_funcs = {
278 	.update_plane = drm_atomic_helper_update_plane,
279 	.disable_plane = drm_atomic_helper_disable_plane,
280 	.reset = shmob_drm_plane_reset,
281 	.atomic_duplicate_state = shmob_drm_plane_atomic_duplicate_state,
282 	.atomic_destroy_state = shmob_drm_plane_atomic_destroy_state,
283 };
284 
285 static const uint32_t formats[] = {
286 	DRM_FORMAT_RGB565,
287 	DRM_FORMAT_RGB888,
288 	DRM_FORMAT_ARGB8888,
289 	DRM_FORMAT_XRGB8888,
290 	DRM_FORMAT_NV12,
291 	DRM_FORMAT_NV21,
292 	DRM_FORMAT_NV16,
293 	DRM_FORMAT_NV61,
294 	DRM_FORMAT_NV24,
295 	DRM_FORMAT_NV42,
296 };
297 
298 struct drm_plane *shmob_drm_plane_create(struct shmob_drm_device *sdev,
299 					 enum drm_plane_type type,
300 					 unsigned int index)
301 {
302 	struct shmob_drm_plane *splane;
303 
304 	splane = drmm_universal_plane_alloc(&sdev->ddev,
305 					    struct shmob_drm_plane, base, 1,
306 					    &shmob_drm_plane_funcs, formats,
307 					    ARRAY_SIZE(formats),  NULL, type,
308 					    NULL);
309 	if (IS_ERR(splane))
310 		return ERR_CAST(splane);
311 
312 	splane->index = index;
313 
314 	drm_plane_helper_add(&splane->base, &shmob_drm_plane_helper_funcs);
315 
316 	return &splane->base;
317 }
318