1 // SPDX-License-Identifier: GPL-2.0+
3 * shmob_drm_plane.c -- SH Mobile DRM Planes
5 * Copyright (C) 2012 Renesas Electronics Corporation
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>
18 #include "shmob_drm_drv.h"
19 #include "shmob_drm_kms.h"
20 #include "shmob_drm_plane.h"
21 #include "shmob_drm_regs.h"
23 struct shmob_drm_plane {
24 struct drm_plane base;
28 struct shmob_drm_plane_state {
29 struct drm_plane_state base;
31 const struct shmob_drm_format_info *format;
35 static inline struct shmob_drm_plane *to_shmob_plane(struct drm_plane *plane)
37 return container_of(plane, struct shmob_drm_plane, base);
40 static inline struct shmob_drm_plane_state *to_shmob_plane_state(struct drm_plane_state *state)
42 return container_of(state, struct shmob_drm_plane_state, base);
45 static void shmob_drm_plane_compute_base(struct shmob_drm_plane_state *sstate)
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;
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;
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);
67 static void shmob_drm_primary_plane_setup(struct shmob_drm_plane *splane,
68 struct drm_plane_state *state)
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;
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]);
78 /* Word and long word swap. */
79 lcdc_write(sdev, LDDDSR, sstate->format->ldddsr);
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]);
85 lcdc_write(sdev, LDRCNTR, lcdc_read(sdev, LDRCNTR) ^ LDRCNTR_MRS);
88 static void shmob_drm_overlay_plane_setup(struct shmob_drm_plane *splane,
89 struct drm_plane_state *state)
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;
96 /* TODO: Support ROP3 mode */
97 format = LDBBSIFR_EN | ((state->alpha >> 8) << LDBBSIFR_LAY_SHIFT) |
98 sstate->format->ldbbsifr;
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))
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);
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));
117 lcdc_write(sdev, LDBnBSIFR(splane->index), format);
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);
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]);
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));
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);
145 static int shmob_drm_plane_atomic_check(struct drm_plane *plane,
146 struct drm_atomic_state *state)
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;
154 if (!new_plane_state->crtc) {
156 * The visible field is not reset by the DRM core but only
157 * updated by drm_atomic_helper_check_plane_state(), set it
160 new_plane_state->visible = false;
161 sstate->format = NULL;
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);
169 ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
170 DRM_PLANE_NO_SCALING,
171 DRM_PLANE_NO_SCALING,
176 if (!new_plane_state->visible) {
177 sstate->format = NULL;
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);
189 shmob_drm_plane_compute_base(sstate);
194 static void shmob_drm_plane_atomic_update(struct drm_plane *plane,
195 struct drm_atomic_state *state)
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);
200 if (!new_plane_state->visible)
203 if (plane->type == DRM_PLANE_TYPE_PRIMARY)
204 shmob_drm_primary_plane_setup(splane, new_plane_state);
206 shmob_drm_overlay_plane_setup(splane, new_plane_state);
209 static void shmob_drm_plane_atomic_disable(struct drm_plane *plane,
210 struct drm_atomic_state *state)
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);
216 if (!old_state->crtc)
219 if (plane->type != DRM_PLANE_TYPE_OVERLAY)
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));
228 static struct drm_plane_state *
229 shmob_drm_plane_atomic_duplicate_state(struct drm_plane *plane)
231 struct shmob_drm_plane_state *state;
232 struct shmob_drm_plane_state *copy;
234 if (WARN_ON(!plane->state))
237 state = to_shmob_plane_state(plane->state);
238 copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
242 __drm_atomic_helper_plane_duplicate_state(plane, ©->base);
247 static void shmob_drm_plane_atomic_destroy_state(struct drm_plane *plane,
248 struct drm_plane_state *state)
250 __drm_atomic_helper_plane_destroy_state(state);
251 kfree(to_shmob_plane_state(state));
254 static void shmob_drm_plane_reset(struct drm_plane *plane)
256 struct shmob_drm_plane_state *state;
259 shmob_drm_plane_atomic_destroy_state(plane, plane->state);
263 state = kzalloc(sizeof(*state), GFP_KERNEL);
267 __drm_atomic_helper_plane_reset(plane, &state->base);
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,
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,
284 static const uint32_t formats[] = {
297 struct drm_plane *shmob_drm_plane_create(struct shmob_drm_device *sdev,
298 enum drm_plane_type type,
301 struct shmob_drm_plane *splane;
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,
309 return ERR_CAST(splane);
311 splane->index = index;
313 drm_plane_helper_add(&splane->base, &shmob_drm_plane_helper_funcs);
315 return &splane->base;