diff options
author | Dave Airlie <airlied@redhat.com> | 2018-02-16 09:29:27 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2018-02-16 09:29:27 +1000 |
commit | 76ea0f334e7fb13226e64ee7de928611f5303faf (patch) | |
tree | 4a6b8d8f1b19a68072f2ce8f33add59be9a67be7 /drivers/gpu/drm/sun4i/sun4i_backend.c | |
parent | f0308d76906a5b65ec4fcc3b133394caa9f00638 (diff) | |
parent | 1bc3d3cce8c3b44c2b5ac6cee98c830bb40e6b0f (diff) | |
download | linux-76ea0f334e7fb13226e64ee7de928611f5303faf.tar.gz linux-76ea0f334e7fb13226e64ee7de928611f5303faf.tar.xz |
Merge tag 'drm-misc-next-2018-02-13' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for 4.17:
UAPI Changes:
- drm/vc4: Expose performance counters to userspace (Boris)
Cross-subsystem Changes:
- MAINTAINERS: Linus to maintain panel-arm-versatile in -misc (Linus)
Core Changes:
- Only use swiotlb when necessary (Chunming)
Driver Changes:
- drm/panel: Add support for ARM Versatile panels (Linus)
- pl111: Improvements around versatile panel support (Linus)
----------------------------------------
Tagged on 2018-02-06:
drm-misc-next for 4.17:
UAPI Changes:
- Validate mode flags + type (Ville)
- Deprecate unused mode flags PIXMUX, BCAST (Ville)
- Deprecate unused mode types BUILTIN, CRTC_C, CLOCK_C, DEFAULT (Ville)
Cross-subsystem Changes:
- MAINTAINERS: s/Daniel/Maarten/ for drm-misc (Daniel)
Core Changes:
- gem: Export gem functions for drivers to use (Samuel)
- bridge: Introduce bridge timings in drm_bridge (Linus)
- dma-buf: Allow exclusive fence to be bundled in fence array when
calling reservation_object_get_fences_rcu (Christian)
- dp: Add training pattern 4 and HBR3 support to dp helpers (Manasi)
- fourcc: Add alpha bit to formats to avoid driver format LUTs (Maxime)
- mode: Various cleanups + add new device-wide .mode_valid hook (Ville)
- atomic: Fix state leak when non-blocking commits fail (Leo)
NOTE: IIRC, this was cross-picked to -fixes so it might fall out
- crc: Allow polling on the data fd (Maarten)
Driver Changes:
- bridge/vga-dac: Add THS8134* support (Linus)
- tinydrm: Various MIPI DBI improvements/cleanups (Noralf)
- bridge/dw-mipi-dsi: Cleanups + use create_packet helper (Brian)
- drm/sun4i: Add Display Engine frontend support (Maxime)
- drm/sun4i: Add zpos support + increase num planes from 2 to 4 (Maxime)
- various: Use drm_mode_get_hv_timing() to fill plane clip rectangle (Ville)
- stm: Add 8-bit clut support, add dsi phy v1.31 support, +fixes (Phillipe)
Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
Cc: Chunming Zhou <david1.zhou@amd.com>
Cc: Samuel Li <Samuel.Li@amd.com>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Noralf Trønnes <noralf@tronnes.org>
Cc: Brian Norris <briannorris@chromium.org>
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Cc: Ville Syrjala <ville.syrjala@linux.intel.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Manasi Navare <manasi.d.navare@intel.com>
Cc: Philippe Cornu <philippe.cornu@st.com>
Cc: Leo (Sunpeng) Li <sunpeng.li@amd.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
* tag 'drm-misc-next-2018-02-13' of git://anongit.freedesktop.org/drm/drm-misc: (115 commits)
drm/radeon: only enable swiotlb path when need v2
drm/amdgpu: only enable swiotlb alloc when need v2
drm: add func to get max iomem address v2
drm/vc4: Expose performance counters to userspace
drm: Print the pid when debug logging an ioctl error.
drm/stm: ltdc: remove non-alpha color formats on layer 2 for older hw
drm/stm: ltdc: add non-alpha color formats
drm/bridge/synopsys: dsi: Add 1.31 version support
drm/bridge/synopsys: dsi: Add read feature
drm/pl111: Support multiple endpoints on the CLCD
drm/pl111: Support variants with broken VBLANK
drm/pl111: Support variants with broken clock divider
drm/pl111: Handle the Versatile RGB/BGR565 mode
drm/pl111: Properly detect the ARM PL110 variants
drm/panel: Add support for ARM Versatile panels
drm/panel: Device tree bindings for ARM Versatile panels
drm/bridge: Rename argument from crtc to bridge
drm/crc: Add support for polling on the data fd.
drm/sun4i: Use drm_mode_get_hv_timing() to populate plane clip rectangle
drm/rcar-du: Use drm_mode_get_hv_timing() to populate plane clip rectangle
...
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 @@ */ #include <drm/drmP.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> @@ -26,6 +27,7 @@ #include "sun4i_backend.h" #include "sun4i_drv.h" +#include "sun4i_frontend.h" #include "sun4i_layer.h" #include "sunxi_engine.h" @@ -93,7 +95,7 @@ void sun4i_backend_layer_enable(struct sun4i_backend *backend, static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane, u32 format, u32 *mode) { - if ((plane->type == DRM_PLANE_TYPE_PRIMARY) && + if (plane && (plane->type == DRM_PLANE_TYPE_PRIMARY) && (format == DRM_FORMAT_ARGB8888)) format = DRM_FORMAT_XRGB8888; @@ -141,7 +143,6 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend, int layer, struct drm_plane *plane) { struct drm_plane_state *state = plane->state; - struct drm_framebuffer *fb = state->fb; DRM_DEBUG_DRIVER("Updating layer %d\n", layer); @@ -153,12 +154,6 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend, state->crtc_h)); } - /* Set the line width */ - DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8); - regmap_write(backend->engine.regs, - SUN4I_BACKEND_LAYLINEWIDTH_REG(layer), - fb->pitches[0] * 8); - /* Set height and width */ DRM_DEBUG_DRIVER("Layer size W: %u H: %u\n", state->crtc_w, state->crtc_h); @@ -210,6 +205,30 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend, return 0; } +int sun4i_backend_update_layer_frontend(struct sun4i_backend *backend, + int layer, uint32_t fmt) +{ + u32 val; + int ret; + + ret = sun4i_backend_drm_format_to_layer(NULL, fmt, &val); + if (ret) { + DRM_DEBUG_DRIVER("Invalid format\n"); + return ret; + } + + regmap_update_bits(backend->engine.regs, + SUN4I_BACKEND_ATTCTL_REG0(layer), + SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN, + SUN4I_BACKEND_ATTCTL_REG0_LAY_VDOEN); + + regmap_update_bits(backend->engine.regs, + SUN4I_BACKEND_ATTCTL_REG1(layer), + SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val); + + return 0; +} + int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, int layer, struct drm_plane *plane) { @@ -218,6 +237,12 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, u32 lo_paddr, hi_paddr; dma_addr_t paddr; + /* Set the line width */ + DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8); + regmap_write(backend->engine.regs, + SUN4I_BACKEND_LAYLINEWIDTH_REG(layer), + fb->pitches[0] * 8); + /* Get the start of the displayed memory */ paddr = drm_fb_cma_get_gem_addr(fb, state, 0); DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr); @@ -246,6 +271,176 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend, return 0; } +int sun4i_backend_update_layer_zpos(struct sun4i_backend *backend, int layer, + struct drm_plane *plane) +{ + struct drm_plane_state *state = plane->state; + unsigned int priority = state->normalized_zpos; + + DRM_DEBUG_DRIVER("Setting layer %d's priority to %d\n", layer, priority); + + regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_ATTCTL_REG0(layer), + SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL_MASK, + SUN4I_BACKEND_ATTCTL_REG0_LAY_PRISEL(priority)); + + return 0; +} + +static bool sun4i_backend_plane_uses_scaler(struct drm_plane_state *state) +{ + u16 src_h = state->src_h >> 16; + u16 src_w = state->src_w >> 16; + + DRM_DEBUG_DRIVER("Input size %dx%d, output size %dx%d\n", + src_w, src_h, state->crtc_w, state->crtc_h); + + if ((state->crtc_h != src_h) || (state->crtc_w != src_w)) + return true; + + return false; +} + +static bool sun4i_backend_plane_uses_frontend(struct drm_plane_state *state) +{ + struct sun4i_layer *layer = plane_to_sun4i_layer(state->plane); + struct sun4i_backend *backend = layer->backend; + + if (IS_ERR(backend->frontend)) + return false; + + return sun4i_backend_plane_uses_scaler(state); +} + +static void sun4i_backend_atomic_begin(struct sunxi_engine *engine, + struct drm_crtc_state *old_state) +{ + u32 val; + + WARN_ON(regmap_read_poll_timeout(engine->regs, + SUN4I_BACKEND_REGBUFFCTL_REG, + val, !(val & SUN4I_BACKEND_REGBUFFCTL_LOADCTL), + 100, 50000)); +} + +static int sun4i_backend_atomic_check(struct sunxi_engine *engine, + struct drm_crtc_state *crtc_state) +{ + struct drm_atomic_state *state = crtc_state->state; + struct drm_device *drm = state->dev; + struct drm_plane *plane; + unsigned int num_planes = 0; + unsigned int num_alpha_planes = 0; + unsigned int num_frontend_planes = 0; + + DRM_DEBUG_DRIVER("Starting checking our planes\n"); + + if (!crtc_state->planes_changed) + return 0; + + drm_for_each_plane_mask(plane, drm, crtc_state->plane_mask) { + struct drm_plane_state *plane_state = + drm_atomic_get_plane_state(state, plane); + struct sun4i_layer_state *layer_state = + state_to_sun4i_layer_state(plane_state); + struct drm_framebuffer *fb = plane_state->fb; + struct drm_format_name_buf format_name; + + if (sun4i_backend_plane_uses_frontend(plane_state)) { + DRM_DEBUG_DRIVER("Using the frontend for plane %d\n", + plane->index); + + layer_state->uses_frontend = true; + num_frontend_planes++; + } else { + layer_state->uses_frontend = false; + } + + DRM_DEBUG_DRIVER("Plane FB format is %s\n", + drm_get_format_name(fb->format->format, + &format_name)); + if (fb->format->has_alpha) + num_alpha_planes++; + + num_planes++; + } + + /* + * The hardware is a bit unusual here. + * + * Even though it supports 4 layers, it does the composition + * in two separate steps. + * + * The first one is assigning a layer to one of its two + * pipes. If more that 1 layer is assigned to the same pipe, + * and if pixels overlaps, the pipe will take the pixel from + * the layer with the highest priority. + * + * The second step is the actual alpha blending, that takes + * the two pipes as input, and uses the eventual alpha + * component to do the transparency between the two. + * + * This two steps scenario makes us unable to guarantee a + * robust alpha blending between the 4 layers in all + * situations, since this means that we need to have one layer + * with alpha at the lowest position of our two pipes. + * + * However, we cannot even do that, since the hardware has a + * bug where the lowest plane of the lowest pipe (pipe 0, + * priority 0), if it has any alpha, will discard the pixel + * entirely and just display the pixels in the background + * color (black by default). + * + * This means that we effectively have only three valid + * configurations with alpha, all of them with the alpha being + * on pipe1 with the lowest position, which can be 1, 2 or 3 + * depending on the number of planes and their zpos. + */ + if (num_alpha_planes > SUN4I_BACKEND_NUM_ALPHA_LAYERS) { + DRM_DEBUG_DRIVER("Too many planes with alpha, rejecting...\n"); + return -EINVAL; + } + + if (num_frontend_planes > SUN4I_BACKEND_NUM_FRONTEND_LAYERS) { + DRM_DEBUG_DRIVER("Too many planes going through the frontend, rejecting\n"); + return -EINVAL; + } + + DRM_DEBUG_DRIVER("State valid with %u planes, %u alpha, %u video\n", + num_planes, num_alpha_planes, num_frontend_planes); + + return 0; +} + +static void sun4i_backend_vblank_quirk(struct sunxi_engine *engine) +{ + struct sun4i_backend *backend = engine_to_sun4i_backend(engine); + struct sun4i_frontend *frontend = backend->frontend; + + if (!frontend) + return; + + /* + * In a teardown scenario with the frontend involved, we have + * to keep the frontend enabled until the next vblank, and + * only then disable it. + * + * This is due to the fact that the backend will not take into + * account the new configuration (with the plane that used to + * be fed by the frontend now disabled) until we write to the + * commit bit and the hardware fetches the new configuration + * during the next vblank. + * + * So we keep the frontend around in order to prevent any + * visual artifacts. + */ + spin_lock(&backend->frontend_lock); + if (backend->frontend_teardown) { + sun4i_frontend_exit(frontend); + backend->frontend_teardown = false; + } + spin_unlock(&backend->frontend_lock); +}; + static int sun4i_backend_init_sat(struct device *dev) { struct sun4i_backend *backend = dev_get_drvdata(dev); int ret; @@ -330,11 +525,43 @@ static int sun4i_backend_of_get_id(struct device_node *node) return ret; } +/* TODO: This needs to take multiple pipelines into account */ +static struct sun4i_frontend *sun4i_backend_find_frontend(struct sun4i_drv *drv, + struct device_node *node) +{ + struct device_node *port, *ep, *remote; + struct sun4i_frontend *frontend; + + port = of_graph_get_port_by_id(node, 0); + if (!port) + return ERR_PTR(-EINVAL); + + for_each_available_child_of_node(port, ep) { + remote = of_graph_get_remote_port_parent(ep); + if (!remote) + continue; + + /* does this node match any registered engines? */ + list_for_each_entry(frontend, &drv->frontend_list, list) { + if (remote == frontend->node) { + of_node_put(remote); + of_node_put(port); + return frontend; + } + } + } + + return ERR_PTR(-EINVAL); +} + static const struct sunxi_engine_ops sun4i_backend_engine_ops = { + .atomic_begin = sun4i_backend_atomic_begin, + .atomic_check = sun4i_backend_atomic_check, .commit = sun4i_backend_commit, .layers_init = sun4i_layers_init, .apply_color_correction = sun4i_backend_apply_color_correction, .disable_color_correction = sun4i_backend_disable_color_correction, + .vblank_quirk = sun4i_backend_vblank_quirk, }; static struct regmap_config sun4i_backend_regmap_config = { @@ -360,6 +587,7 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, if (!backend) return -ENOMEM; dev_set_drvdata(dev, backend); + spin_lock_init(&backend->frontend_lock); backend->engine.node = dev->of_node; backend->engine.ops = &sun4i_backend_engine_ops; @@ -367,6 +595,10 @@ static int sun4i_backend_bind(struct device *dev, struct device *master, if (backend->engine.id < 0) return backend->engine.id; + backend->frontend = sun4i_backend_find_frontend(drv, dev->of_node); + if (IS_ERR(backend->frontend)) + dev_warn(dev, "Couldn't find matching frontend, frontend features disabled\n"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = devm_ioremap_resource(dev, res); if (IS_ERR(regs)) |