diff options
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_backend.c')
-rw-r--r-- | drivers/gpu/drm/sun4i/sun4i_backend.c | 248 |
1 files changed, 240 insertions, 8 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c index 847eecbe4d14..245b189fc4d8 100644 --- a/drivers/gpu/drm/sun4i/sun4i_backend.c +++ b/drivers/gpu/drm/sun4i/sun4i_backend.c | |||
@@ -11,6 +11,7 @@ | |||
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <drm/drmP.h> | 13 | #include <drm/drmP.h> |
14 | #include <drm/drm_atomic.h> | ||
14 | #include <drm/drm_atomic_helper.h> | 15 | #include <drm/drm_atomic_helper.h> |
15 | #include <drm/drm_crtc.h> | 16 | #include <drm/drm_crtc.h> |
16 | #include <drm/drm_crtc_helper.h> | 17 | #include <drm/drm_crtc_helper.h> |
@@ -26,6 +27,7 @@ | |||
26 | 27 | ||
27 | #include "sun4i_backend.h" | 28 | #include "sun4i_backend.h" |
28 | #include "sun4i_drv.h" | 29 | #include "sun4i_drv.h" |
30 | #include "sun4i_frontend.h" | ||
29 | #include "sun4i_layer.h" | 31 | #include "sun4i_layer.h" |
30 | #include "sunxi_engine.h" | 32 | #include "sunxi_engine.h" |
31 | 33 | ||
@@ -93,7 +95,7 @@ void sun4i_backend_layer_enable(struct sun4i_backend *backend, | |||
93 | static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane, | 95 | static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane, |
94 | u32 format, u32 *mode) | 96 | u32 format, u32 *mode) |
95 | { | 97 | { |
96 | if ((plane->type == DRM_PLANE_TYPE_PRIMARY) && | 98 | if (plane && (plane->type == DRM_PLANE_TYPE_PRIMARY) && |
97 | (format == DRM_FORMAT_ARGB8888)) | 99 | (format == DRM_FORMAT_ARGB8888)) |
98 | format = DRM_FORMAT_XRGB8888; | 100 | format = DRM_FORMAT_XRGB8888; |
99 | 101 | ||
@@ -141,7 +143,6 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend, | |||
141 | int layer, struct drm_plane *plane) | 143 | int layer, struct drm_plane *plane) |
142 | { | 144 | { |
143 | struct drm_plane_state *state = plane->state; | 145 | struct drm_plane_state *state = plane->state; |
144 | struct drm_framebuffer *fb = state->fb; | ||
145 | 146 | ||
146 | DRM_DEBUG_DRIVER("Updating layer %d\n", layer); | 147 | DRM_DEBUG_DRIVER("Updating layer %d\n", layer); |
147 | 148 | ||
@@ -153,12 +154,6 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend, | |||
153 | state->crtc_h)); | 154 | state->crtc_h)); |
154 | } | 155 | } |
155 | 156 | ||
156 | /* Set the line width */ | ||
157 | DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8); | ||
158 | regmap_write(backend->engine.regs, | ||
159 | SUN4I_BACKEND_LAYLINEWIDTH_REG(layer), | ||
160 | fb->pitches[0] * 8); | ||
161 | |||
162 | /* Set height and width */ | 157 | /* Set height and width */ |
163 | DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", | 158 | DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", |
164 | state->crtc_w, state->crtc_h); | 159 | state->crtc_w, state->crtc_h); |
@@ -210,6 +205,30 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, | |||
210 | return 0; | 205 | return 0; |
211 | } | 206 | } |
212 | 207 | ||
208 | int sun4i_backend_update_layer_frontend(struct sun4i_backend *backend, | ||
209 | int layer, uint32_t fmt) | ||
210 | { | ||
211 | u32 val; | ||
212 | int ret; | ||
213 | |||
214 | ret = sun4i_backend_drm_format_to_layer(NULL, fmt, &val); | ||
215 | if (ret) { | ||
216 | DRM_DEBUG_DRIVER("Invalid format\n"); | ||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | regmap_update_bits(backend->engine.regs, | ||
221 | SUN4I_BACKEND_ATTCTL_REG0(layer), | ||
222 | SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN, | ||
223 | SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN); | ||
224 | |||
225 | regmap_update_bits(backend->engine.regs, | ||
226 | SUN4I_BACKEND_ATTCTL_REG1(layer), | ||
227 | SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val); | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
213 | int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, | 232 | int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, |
214 | int layer, struct drm_plane *plane) | 233 | int layer, struct drm_plane *plane) |
215 | { | 234 | { |
@@ -218,6 +237,12 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, | |||
218 | u32 lo_paddr, hi_paddr; | 237 | u32 lo_paddr, hi_paddr; |
219 | dma_addr_t paddr; | 238 | dma_addr_t paddr; |
220 | 239 | ||
240 | /* Set the line width */ | ||
241 | DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8); | ||
242 | regmap_write(backend->engine.regs, | ||
243 | SUN4I_BACKEND_LAYLINEWIDTH_REG(layer), | ||
244 | fb->pitches[0] * 8); | ||
245 | |||
221 | /* Get the start of the displayed memory */ | 246 | /* Get the start of the displayed memory */ |
222 | paddr = drm_fb_cma_get_gem_addr(fb, state, 0); | 247 | paddr = drm_fb_cma_get_gem_addr(fb, state, 0); |
223 | DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); | 248 | DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); |
@@ -246,6 +271,176 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, | |||
246 | return 0; | 271 | return 0; |
247 | } | 272 | } |
248 | 273 | ||
274 | int sun4i_backend_update_layer_zpos(struct sun4i_backend *backend, int layer, | ||
275 | struct drm_plane *plane) | ||
276 | { | ||
277 | struct drm_plane_state *state = plane->state; | ||
278 | unsigned int priority = state->normalized_zpos; | ||
279 | |||
280 | DRM_DEBUG_DRIVER("Setting layer %d's priority to %d\n", layer, priority); | ||
281 | |||
282 | regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_ATTCTL_REG0(layer), | ||
283 | SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK, | ||
284 | SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(priority)); | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static bool sun4i_backend_plane_uses_scaler(struct drm_plane_state *state) | ||
290 | { | ||
291 | u16 src_h = state->src_h >> 16; | ||
292 | u16 src_w = state->src_w >> 16; | ||
293 | |||
294 | DRM_DEBUG_DRIVER("Input size %dx%d, output size %dx%d\n", | ||
295 | src_w, src_h, state->crtc_w, state->crtc_h); | ||
296 | |||
297 | if ((state->crtc_h != src_h) || (state->crtc_w != src_w)) | ||
298 | return true; | ||
299 | |||
300 | return false; | ||
301 | } | ||
302 | |||
303 | static bool sun4i_backend_plane_uses_frontend(struct drm_plane_state *state) | ||
304 | { | ||
305 | struct sun4i_layer *layer = plane_to_sun4i_layer(state->plane); | ||
306 | struct sun4i_backend *backend = layer->backend; | ||
307 | |||
308 | if (IS_ERR(backend->frontend)) | ||
309 | return false; | ||
310 | |||
311 | return sun4i_backend_plane_uses_scaler(state); | ||
312 | } | ||
313 | |||
314 | static void sun4i_backend_atomic_begin(struct sunxi_engine *engine, | ||
315 | struct drm_crtc_state *old_state) | ||
316 | { | ||
317 | u32 val; | ||
318 | |||
319 | WARN_ON(regmap_read_poll_timeout(engine->regs, | ||
320 | SUN4I_BACKEND_REGBUFFCTL_REG, | ||
321 | val, !(val & SUN4I_BACKEND_REGBUFFCTL_LOADCTL), | ||
322 | 100, 50000)); | ||
323 | } | ||
324 | |||
325 | static int sun4i_backend_atomic_check(struct sunxi_engine *engine, | ||
326 | struct drm_crtc_state *crtc_state) | ||
327 | { | ||
328 | struct drm_atomic_state *state = crtc_state->state; | ||
329 | struct drm_device *drm = state->dev; | ||
330 | struct drm_plane *plane; | ||
331 | unsigned int num_planes = 0; | ||
332 | unsigned int num_alpha_planes = 0; | ||
333 | unsigned int num_frontend_planes = 0; | ||
334 | |||
335 | DRM_DEBUG_DRIVER("Starting checking our planes\n"); | ||
336 | |||
337 | if (!crtc_state->planes_changed) | ||
338 | return 0; | ||
339 | |||
340 | drm_for_each_plane_mask(plane, drm, crtc_state->plane_mask) { | ||
341 | struct drm_plane_state *plane_state = | ||
342 | drm_atomic_get_plane_state(state, plane); | ||
343 | struct sun4i_layer_state *layer_state = | ||
344 | state_to_sun4i_layer_state(plane_state); | ||
345 | struct drm_framebuffer *fb = plane_state->fb; | ||
346 | struct drm_format_name_buf format_name; | ||
347 | |||
348 | if (sun4i_backend_plane_uses_frontend(plane_state)) { | ||
349 | DRM_DEBUG_DRIVER("Using the frontend for plane %d\n", | ||
350 | plane->index); | ||
351 | |||
352 | layer_state->uses_frontend = true; | ||
353 | num_frontend_planes++; | ||
354 | } else { | ||
355 | layer_state->uses_frontend = false; | ||
356 | } | ||
357 | |||
358 | DRM_DEBUG_DRIVER("Plane FB format is %s\n", | ||
359 | drm_get_format_name(fb->format->format, | ||
360 | &format_name)); | ||
361 | if (fb->format->has_alpha) | ||
362 | num_alpha_planes++; | ||
363 | |||
364 | num_planes++; | ||
365 | } | ||
366 | |||
367 | /* | ||
368 | * The hardware is a bit unusual here. | ||
369 | * | ||
370 | * Even though it supports 4 layers, it does the composition | ||
371 | * in two separate steps. | ||
372 | * | ||
373 | * The first one is assigning a layer to one of its two | ||
374 | * pipes. If more that 1 layer is assigned to the same pipe, | ||
375 | * and if pixels overlaps, the pipe will take the pixel from | ||
376 | * the layer with the highest priority. | ||
377 | * | ||
378 | * The second step is the actual alpha blending, that takes | ||
379 | * the two pipes as input, and uses the eventual alpha | ||
380 | * component to do the transparency between the two. | ||
381 | * | ||
382 | * This two steps scenario makes us unable to guarantee a | ||
383 | * robust alpha blending between the 4 layers in all | ||
384 | * situations, since this means that we need to have one layer | ||
385 | * with alpha at the lowest position of our two pipes. | ||
386 | * | ||
387 | * However, we cannot even do that, since the hardware has a | ||
388 | * bug where the lowest plane of the lowest pipe (pipe 0, | ||
389 | * priority 0), if it has any alpha, will discard the pixel | ||
390 | * entirely and just display the pixels in the background | ||
391 | * color (black by default). | ||
392 | * | ||
393 | * This means that we effectively have only three valid | ||
394 | * configurations with alpha, all of them with the alpha being | ||
395 | * on pipe1 with the lowest position, which can be 1, 2 or 3 | ||
396 | * depending on the number of planes and their zpos. | ||
397 | */ | ||
398 | if (num_alpha_planes > SUN4I_BACKEND_NUM_ALPHA_LAYERS) { | ||
399 | DRM_DEBUG_DRIVER("Too many planes with alpha, rejecting...\n"); | ||
400 | return -EINVAL; | ||
401 | } | ||
402 | |||
403 | if (num_frontend_planes > SUN4I_BACKEND_NUM_FRONTEND_LAYERS) { | ||
404 | DRM_DEBUG_DRIVER("Too many planes going through the frontend, rejecting\n"); | ||
405 | return -EINVAL; | ||
406 | } | ||
407 | |||
408 | DRM_DEBUG_DRIVER("State valid with %u planes, %u alpha, %u video\n", | ||
409 | num_planes, num_alpha_planes, num_frontend_planes); | ||
410 | |||
411 | return 0; | ||
412 | } | ||
413 | |||
414 | static void sun4i_backend_vblank_quirk(struct sunxi_engine *engine) | ||
415 | { | ||
416 | struct sun4i_backend *backend = engine_to_sun4i_backend(engine); | ||
417 | struct sun4i_frontend *frontend = backend->frontend; | ||
418 | |||
419 | if (!frontend) | ||
420 | return; | ||
421 | |||
422 | /* | ||
423 | * In a teardown scenario with the frontend involved, we have | ||
424 | * to keep the frontend enabled until the next vblank, and | ||
425 | * only then disable it. | ||
426 | * | ||
427 | * This is due to the fact that the backend will not take into | ||
428 | * account the new configuration (with the plane that used to | ||
429 | * be fed by the frontend now disabled) until we write to the | ||
430 | * commit bit and the hardware fetches the new configuration | ||
431 | * during the next vblank. | ||
432 | * | ||
433 | * So we keep the frontend around in order to prevent any | ||
434 | * visual artifacts. | ||
435 | */ | ||
436 | spin_lock(&backend->frontend_lock); | ||
437 | if (backend->frontend_teardown) { | ||
438 | sun4i_frontend_exit(frontend); | ||
439 | backend->frontend_teardown = false; | ||
440 | } | ||
441 | spin_unlock(&backend->frontend_lock); | ||
442 | }; | ||
443 | |||
249 | static int sun4i_backend_init_sat(struct device *dev) { | 444 | static int sun4i_backend_init_sat(struct device *dev) { |
250 | struct sun4i_backend *backend = dev_get_drvdata(dev); | 445 | struct sun4i_backend *backend = dev_get_drvdata(dev); |
251 | int ret; | 446 | int ret; |
@@ -330,11 +525,43 @@ static int sun4i_backend_of_get_id(struct device_node *node) | |||
330 | return ret; | 525 | return ret; |
331 | } | 526 | } |
332 | 527 | ||
528 | /* TODO: This needs to take multiple pipelines into account */ | ||
529 | static struct sun4i_frontend *sun4i_backend_find_frontend(struct sun4i_drv *drv, | ||
530 | struct device_node *node) | ||
531 | { | ||
532 | struct device_node *port, *ep, *remote; | ||
533 | struct sun4i_frontend *frontend; | ||
534 | |||
535 | port = of_graph_get_port_by_id(node, 0); | ||
536 | if (!port) | ||
537 | return ERR_PTR(-EINVAL); | ||
538 | |||
539 | for_each_available_child_of_node(port, ep) { | ||
540 | remote = of_graph_get_remote_port_parent(ep); | ||
541 | if (!remote) | ||
542 | continue; | ||
543 | |||
544 | /* does this node match any registered engines? */ | ||
545 | list_for_each_entry(frontend, &drv->frontend_list, list) { | ||
546 | if (remote == frontend->node) { | ||
547 | of_node_put(remote); | ||
548 | of_node_put(port); | ||
549 | return frontend; | ||
550 | } | ||
551 | } | ||
552 | } | ||
553 | |||
554 | return ERR_PTR(-EINVAL); | ||
555 | } | ||
556 | |||
333 | static const struct sunxi_engine_ops sun4i_backend_engine_ops = { | 557 | static const struct sunxi_engine_ops sun4i_backend_engine_ops = { |
558 | .atomic_begin = sun4i_backend_atomic_begin, | ||
559 | .atomic_check = sun4i_backend_atomic_check, | ||
334 | .commit = sun4i_backend_commit, | 560 | .commit = sun4i_backend_commit, |
335 | .layers_init = sun4i_layers_init, | 561 | .layers_init = sun4i_layers_init, |
336 | .apply_color_correction = sun4i_backend_apply_color_correction, | 562 | .apply_color_correction = sun4i_backend_apply_color_correction, |
337 | .disable_color_correction = sun4i_backend_disable_color_correction, | 563 | .disable_color_correction = sun4i_backend_disable_color_correction, |
564 | .vblank_quirk = sun4i_backend_vblank_quirk, | ||
338 | }; | 565 | }; |
339 | 566 | ||
340 | static struct regmap_config sun4i_backend_regmap_config = { | 567 | static struct regmap_config sun4i_backend_regmap_config = { |
@@ -360,6 +587,7 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, | |||
360 | if (!backend) | 587 | if (!backend) |
361 | return -ENOMEM; | 588 | return -ENOMEM; |
362 | dev_set_drvdata(dev, backend); | 589 | dev_set_drvdata(dev, backend); |
590 | spin_lock_init(&backend->frontend_lock); | ||
363 | 591 | ||
364 | backend->engine.node = dev->of_node; | 592 | backend->engine.node = dev->of_node; |
365 | backend->engine.ops = &sun4i_backend_engine_ops; | 593 | backend->engine.ops = &sun4i_backend_engine_ops; |
@@ -367,6 +595,10 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, | |||
367 | if (backend->engine.id < 0) | 595 | if (backend->engine.id < 0) |
368 | return backend->engine.id; | 596 | return backend->engine.id; |
369 | 597 | ||
598 | backend->frontend = sun4i_backend_find_frontend(drv, dev->of_node); | ||
599 | if (IS_ERR(backend->frontend)) | ||
600 | dev_warn(dev, "Couldn't find matching frontend, frontend features disabled\n"); | ||
601 | |||
370 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 602 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
371 | regs = devm_ioremap_resource(dev, res); | 603 | regs = devm_ioremap_resource(dev, res); |
372 | if (IS_ERR(regs)) | 604 | if (IS_ERR(regs)) |