summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/sun4i/sun4i_backend.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2018-02-16 09:29:27 +1000
committerDave Airlie <airlied@redhat.com>2018-02-16 09:29:27 +1000
commit76ea0f334e7fb13226e64ee7de928611f5303faf (patch)
tree4a6b8d8f1b19a68072f2ce8f33add59be9a67be7 /drivers/gpu/drm/sun4i/sun4i_backend.c
parentf0308d76906a5b65ec4fcc3b133394caa9f00638 (diff)
parent1bc3d3cce8c3b44c2b5ac6cee98c830bb40e6b0f (diff)
downloadlinux-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.c248
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))