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