blob: ed07ee57e1bfc8ceaf2df4e0e29019fb28c54d45 [file] [log] [blame]
Eric Anholtc8b75bc2015-03-02 13:01:12 -08001/*
2 * Copyright (C) 2015 Broadcom
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9/**
10 * DOC: VC4 plane module
11 *
12 * Each DRM plane is a layer of pixels being scanned out by the HVS.
13 *
14 * At atomic modeset check time, we compute the HVS display element
15 * state that would be necessary for displaying the plane (giving us a
16 * chance to figure out if a plane configuration is invalid), then at
17 * atomic flush time the CRTC will ask us to write our element state
18 * into the region of the HVS that it has allocated for us.
19 */
20
21#include "vc4_drv.h"
22#include "vc4_regs.h"
23#include "drm_atomic_helper.h"
24#include "drm_fb_cma_helper.h"
25#include "drm_plane_helper.h"
26
27struct vc4_plane_state {
28 struct drm_plane_state base;
Eric Anholtf427fb12015-12-28 14:14:09 -080029 /* System memory copy of the display list for this element, computed
30 * at atomic_check time.
31 */
Eric Anholtc8b75bc2015-03-02 13:01:12 -080032 u32 *dlist;
Eric Anholtf427fb12015-12-28 14:14:09 -080033 u32 dlist_size; /* Number of dwords allocated for the display list */
Eric Anholtc8b75bc2015-03-02 13:01:12 -080034 u32 dlist_count; /* Number of used dwords in the display list. */
Eric Anholtb501bac2015-11-30 12:34:01 -080035
36 /* Offset in the dlist to pointer word 0. */
37 u32 pw0_offset;
38
39 /* Offset where the plane's dlist was last stored in the
Eric Anholtf427fb12015-12-28 14:14:09 -080040 * hardware at vc4_crtc_atomic_flush() time.
41 */
Eric Anholt17eac752015-12-28 14:14:57 -080042 u32 __iomem *hw_dlist;
Eric Anholtc8b75bc2015-03-02 13:01:12 -080043};
44
45static inline struct vc4_plane_state *
46to_vc4_plane_state(struct drm_plane_state *state)
47{
48 return (struct vc4_plane_state *)state;
49}
50
51static const struct hvs_format {
52 u32 drm; /* DRM_FORMAT_* */
53 u32 hvs; /* HVS_FORMAT_* */
54 u32 pixel_order;
55 bool has_alpha;
56} hvs_formats[] = {
57 {
58 .drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
59 .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
60 },
61 {
62 .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
63 .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
64 },
65};
66
67static const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
68{
69 unsigned i;
70
71 for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
72 if (hvs_formats[i].drm == drm_format)
73 return &hvs_formats[i];
74 }
75
76 return NULL;
77}
78
79static bool plane_enabled(struct drm_plane_state *state)
80{
81 return state->fb && state->crtc;
82}
83
kbuild test robot91276ae2015-10-22 11:12:26 +080084static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
Eric Anholtc8b75bc2015-03-02 13:01:12 -080085{
86 struct vc4_plane_state *vc4_state;
87
88 if (WARN_ON(!plane->state))
89 return NULL;
90
91 vc4_state = kmemdup(plane->state, sizeof(*vc4_state), GFP_KERNEL);
92 if (!vc4_state)
93 return NULL;
94
95 __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
96
97 if (vc4_state->dlist) {
98 vc4_state->dlist = kmemdup(vc4_state->dlist,
99 vc4_state->dlist_count * 4,
100 GFP_KERNEL);
101 if (!vc4_state->dlist) {
102 kfree(vc4_state);
103 return NULL;
104 }
105 vc4_state->dlist_size = vc4_state->dlist_count;
106 }
107
108 return &vc4_state->base;
109}
110
kbuild test robot91276ae2015-10-22 11:12:26 +0800111static void vc4_plane_destroy_state(struct drm_plane *plane,
112 struct drm_plane_state *state)
Eric Anholtc8b75bc2015-03-02 13:01:12 -0800113{
114 struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
115
116 kfree(vc4_state->dlist);
117 __drm_atomic_helper_plane_destroy_state(plane, &vc4_state->base);
118 kfree(state);
119}
120
121/* Called during init to allocate the plane's atomic state. */
kbuild test robot91276ae2015-10-22 11:12:26 +0800122static void vc4_plane_reset(struct drm_plane *plane)
Eric Anholtc8b75bc2015-03-02 13:01:12 -0800123{
124 struct vc4_plane_state *vc4_state;
125
126 WARN_ON(plane->state);
127
128 vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
129 if (!vc4_state)
130 return;
131
132 plane->state = &vc4_state->base;
133 vc4_state->base.plane = plane;
134}
135
136static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val)
137{
138 if (vc4_state->dlist_count == vc4_state->dlist_size) {
139 u32 new_size = max(4u, vc4_state->dlist_count * 2);
140 u32 *new_dlist = kmalloc(new_size * 4, GFP_KERNEL);
141
142 if (!new_dlist)
143 return;
144 memcpy(new_dlist, vc4_state->dlist, vc4_state->dlist_count * 4);
145
146 kfree(vc4_state->dlist);
147 vc4_state->dlist = new_dlist;
148 vc4_state->dlist_size = new_size;
149 }
150
151 vc4_state->dlist[vc4_state->dlist_count++] = val;
152}
153
154/* Writes out a full display list for an active plane to the plane's
155 * private dlist state.
156 */
157static int vc4_plane_mode_set(struct drm_plane *plane,
158 struct drm_plane_state *state)
159{
160 struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
161 struct drm_framebuffer *fb = state->fb;
162 struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
163 u32 ctl0_offset = vc4_state->dlist_count;
164 const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format);
165 uint32_t offset = fb->offsets[0];
166 int crtc_x = state->crtc_x;
167 int crtc_y = state->crtc_y;
168 int crtc_w = state->crtc_w;
169 int crtc_h = state->crtc_h;
170
Eric Anholtbf893ac2015-10-23 10:36:27 +0100171 if (state->crtc_w << 16 != state->src_w ||
172 state->crtc_h << 16 != state->src_h) {
173 /* We don't support scaling yet, which involves
174 * allocating the LBM memory for scaling temporary
175 * storage, and putting filter kernels in the HVS
176 * context.
177 */
178 return -EINVAL;
179 }
180
Eric Anholtc8b75bc2015-03-02 13:01:12 -0800181 if (crtc_x < 0) {
182 offset += drm_format_plane_cpp(fb->pixel_format, 0) * -crtc_x;
183 crtc_w += crtc_x;
184 crtc_x = 0;
185 }
186
187 if (crtc_y < 0) {
188 offset += fb->pitches[0] * -crtc_y;
189 crtc_h += crtc_y;
190 crtc_y = 0;
191 }
192
193 vc4_dlist_write(vc4_state,
194 SCALER_CTL0_VALID |
195 (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
196 (format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
197 SCALER_CTL0_UNITY);
198
199 /* Position Word 0: Image Positions and Alpha Value */
200 vc4_dlist_write(vc4_state,
201 VC4_SET_FIELD(0xff, SCALER_POS0_FIXED_ALPHA) |
202 VC4_SET_FIELD(crtc_x, SCALER_POS0_START_X) |
203 VC4_SET_FIELD(crtc_y, SCALER_POS0_START_Y));
204
205 /* Position Word 1: Scaled Image Dimensions.
206 * Skipped due to SCALER_CTL0_UNITY scaling.
207 */
208
209 /* Position Word 2: Source Image Size, Alpha Mode */
210 vc4_dlist_write(vc4_state,
211 VC4_SET_FIELD(format->has_alpha ?
212 SCALER_POS2_ALPHA_MODE_PIPELINE :
213 SCALER_POS2_ALPHA_MODE_FIXED,
214 SCALER_POS2_ALPHA_MODE) |
215 VC4_SET_FIELD(crtc_w, SCALER_POS2_WIDTH) |
216 VC4_SET_FIELD(crtc_h, SCALER_POS2_HEIGHT));
217
218 /* Position Word 3: Context. Written by the HVS. */
219 vc4_dlist_write(vc4_state, 0xc0c0c0c0);
220
Eric Anholtb501bac2015-11-30 12:34:01 -0800221 vc4_state->pw0_offset = vc4_state->dlist_count;
222
Eric Anholtc8b75bc2015-03-02 13:01:12 -0800223 /* Pointer Word 0: RGB / Y Pointer */
224 vc4_dlist_write(vc4_state, bo->paddr + offset);
225
226 /* Pointer Context Word 0: Written by the HVS */
227 vc4_dlist_write(vc4_state, 0xc0c0c0c0);
228
229 /* Pitch word 0: Pointer 0 Pitch */
230 vc4_dlist_write(vc4_state,
231 VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH));
232
233 vc4_state->dlist[ctl0_offset] |=
234 VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE);
235
236 return 0;
237}
238
239/* If a modeset involves changing the setup of a plane, the atomic
240 * infrastructure will call this to validate a proposed plane setup.
241 * However, if a plane isn't getting updated, this (and the
242 * corresponding vc4_plane_atomic_update) won't get called. Thus, we
243 * compute the dlist here and have all active plane dlists get updated
244 * in the CRTC's flush.
245 */
246static int vc4_plane_atomic_check(struct drm_plane *plane,
247 struct drm_plane_state *state)
248{
249 struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
250
251 vc4_state->dlist_count = 0;
252
253 if (plane_enabled(state))
254 return vc4_plane_mode_set(plane, state);
255 else
256 return 0;
257}
258
259static void vc4_plane_atomic_update(struct drm_plane *plane,
260 struct drm_plane_state *old_state)
261{
262 /* No contents here. Since we don't know where in the CRTC's
263 * dlist we should be stored, our dlist is uploaded to the
264 * hardware with vc4_plane_write_dlist() at CRTC atomic_flush
265 * time.
266 */
267}
268
269u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
270{
271 struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
272 int i;
273
Eric Anholtb501bac2015-11-30 12:34:01 -0800274 vc4_state->hw_dlist = dlist;
275
Eric Anholtc8b75bc2015-03-02 13:01:12 -0800276 /* Can't memcpy_toio() because it needs to be 32-bit writes. */
277 for (i = 0; i < vc4_state->dlist_count; i++)
278 writel(vc4_state->dlist[i], &dlist[i]);
279
280 return vc4_state->dlist_count;
281}
282
283u32 vc4_plane_dlist_size(struct drm_plane_state *state)
284{
285 struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
286
287 return vc4_state->dlist_count;
288}
289
Eric Anholtb501bac2015-11-30 12:34:01 -0800290/* Updates the plane to immediately (well, once the FIFO needs
291 * refilling) scan out from at a new framebuffer.
292 */
293void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb)
294{
295 struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
296 struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
297 uint32_t addr;
298
299 /* We're skipping the address adjustment for negative origin,
300 * because this is only called on the primary plane.
301 */
302 WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0);
303 addr = bo->paddr + fb->offsets[0];
304
305 /* Write the new address into the hardware immediately. The
306 * scanout will start from this address as soon as the FIFO
307 * needs to refill with pixels.
308 */
309 writel(addr, &vc4_state->hw_dlist[vc4_state->pw0_offset]);
310
311 /* Also update the CPU-side dlist copy, so that any later
312 * atomic updates that don't do a new modeset on our plane
313 * also use our updated address.
314 */
315 vc4_state->dlist[vc4_state->pw0_offset] = addr;
316}
317
Eric Anholtc8b75bc2015-03-02 13:01:12 -0800318static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
319 .prepare_fb = NULL,
320 .cleanup_fb = NULL,
321 .atomic_check = vc4_plane_atomic_check,
322 .atomic_update = vc4_plane_atomic_update,
323};
324
325static void vc4_plane_destroy(struct drm_plane *plane)
326{
327 drm_plane_helper_disable(plane);
328 drm_plane_cleanup(plane);
329}
330
331static const struct drm_plane_funcs vc4_plane_funcs = {
332 .update_plane = drm_atomic_helper_update_plane,
333 .disable_plane = drm_atomic_helper_disable_plane,
334 .destroy = vc4_plane_destroy,
335 .set_property = NULL,
336 .reset = vc4_plane_reset,
337 .atomic_duplicate_state = vc4_plane_duplicate_state,
338 .atomic_destroy_state = vc4_plane_destroy_state,
339};
340
341struct drm_plane *vc4_plane_init(struct drm_device *dev,
342 enum drm_plane_type type)
343{
344 struct drm_plane *plane = NULL;
345 struct vc4_plane *vc4_plane;
346 u32 formats[ARRAY_SIZE(hvs_formats)];
347 int ret = 0;
348 unsigned i;
349
350 vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
351 GFP_KERNEL);
352 if (!vc4_plane) {
353 ret = -ENOMEM;
354 goto fail;
355 }
356
357 for (i = 0; i < ARRAY_SIZE(hvs_formats); i++)
358 formats[i] = hvs_formats[i].drm;
359 plane = &vc4_plane->base;
360 ret = drm_universal_plane_init(dev, plane, 0xff,
361 &vc4_plane_funcs,
362 formats, ARRAY_SIZE(formats),
Ville Syrjäläb0b3b792015-12-09 16:19:55 +0200363 type, NULL);
Eric Anholtc8b75bc2015-03-02 13:01:12 -0800364
365 drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
366
367 return plane;
368fail:
369 if (plane)
370 vc4_plane_destroy(plane);
371
372 return ERR_PTR(ret);
373}