Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  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
 23struct shmob_drm_plane {
 24	struct drm_plane base;
 25	unsigned int index;
 26};
 27
 28struct 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
 35static 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
 40static 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
 45static 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
 67static 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
 88static 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
145static 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
194static 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
209static 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
228static struct drm_plane_state *
229shmob_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
247static 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
254static 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
270static 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
276static const struct drm_plane_helper_funcs shmob_drm_primary_plane_helper_funcs = {
277	.atomic_check = shmob_drm_plane_atomic_check,
278	.atomic_update = shmob_drm_plane_atomic_update,
279	.atomic_disable = shmob_drm_plane_atomic_disable,
280	.get_scanout_buffer = drm_fb_dma_get_scanout_buffer,
281};
282
283static const struct drm_plane_funcs shmob_drm_plane_funcs = {
284	.update_plane = drm_atomic_helper_update_plane,
285	.disable_plane = drm_atomic_helper_disable_plane,
286	.reset = shmob_drm_plane_reset,
287	.atomic_duplicate_state = shmob_drm_plane_atomic_duplicate_state,
288	.atomic_destroy_state = shmob_drm_plane_atomic_destroy_state,
289};
290
291static const uint32_t formats[] = {
292	DRM_FORMAT_RGB565,
293	DRM_FORMAT_RGB888,
294	DRM_FORMAT_ARGB8888,
295	DRM_FORMAT_XRGB8888,
296	DRM_FORMAT_NV12,
297	DRM_FORMAT_NV21,
298	DRM_FORMAT_NV16,
299	DRM_FORMAT_NV61,
300	DRM_FORMAT_NV24,
301	DRM_FORMAT_NV42,
302};
303
304struct drm_plane *shmob_drm_plane_create(struct shmob_drm_device *sdev,
305					 enum drm_plane_type type,
306					 unsigned int index)
307{
308	struct shmob_drm_plane *splane;
309
310	splane = drmm_universal_plane_alloc(&sdev->ddev,
311					    struct shmob_drm_plane, base, 1,
312					    &shmob_drm_plane_funcs, formats,
313					    ARRAY_SIZE(formats),  NULL, type,
314					    NULL);
315	if (IS_ERR(splane))
316		return ERR_CAST(splane);
317
318	splane->index = index;
319
320	if (type == DRM_PLANE_TYPE_PRIMARY)
321		drm_plane_helper_add(&splane->base,
322				     &shmob_drm_primary_plane_helper_funcs);
323	else
324		drm_plane_helper_add(&splane->base,
325				     &shmob_drm_plane_helper_funcs);
326
327	return &splane->base;
328}