summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/sun4i/sun4i_backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_backend.c')
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_backend.c122
1 files changed, 89 insertions, 33 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_backend.c b/drivers/gpu/drm/sun4i/sun4i_backend.c
index d660741ba475..cf480218daa5 100644
--- a/drivers/gpu/drm/sun4i/sun4i_backend.c
+++ b/drivers/gpu/drm/sun4i/sun4i_backend.c
@@ -19,10 +19,14 @@
#include <drm/drm_plane_helper.h>
#include <linux/component.h>
+#include <linux/list.h>
+#include <linux/of_graph.h>
#include <linux/reset.h>
#include "sun4i_backend.h"
#include "sun4i_drv.h"
+#include "sun4i_layer.h"
+#include "sunxi_engine.h"
static const u32 sunxi_rgb2yuv_coef[12] = {
0x00000107, 0x00000204, 0x00000064, 0x00000108,
@@ -30,58 +34,55 @@ static const u32 sunxi_rgb2yuv_coef[12] = {
0x000001c1, 0x00003e88, 0x00003fb8, 0x00000808
};
-void sun4i_backend_apply_color_correction(struct sun4i_backend *backend)
+static void sun4i_backend_apply_color_correction(struct sunxi_engine *engine)
{
int i;
DRM_DEBUG_DRIVER("Applying RGB to YUV color correction\n");
/* Set color correction */
- regmap_write(backend->regs, SUN4I_BACKEND_OCCTL_REG,
+ regmap_write(engine->regs, SUN4I_BACKEND_OCCTL_REG,
SUN4I_BACKEND_OCCTL_ENABLE);
for (i = 0; i < 12; i++)
- regmap_write(backend->regs, SUN4I_BACKEND_OCRCOEF_REG(i),
+ regmap_write(engine->regs, SUN4I_BACKEND_OCRCOEF_REG(i),
sunxi_rgb2yuv_coef[i]);
}
-EXPORT_SYMBOL(sun4i_backend_apply_color_correction);
-void sun4i_backend_disable_color_correction(struct sun4i_backend *backend)
+static void sun4i_backend_disable_color_correction(struct sunxi_engine *engine)
{
DRM_DEBUG_DRIVER("Disabling color correction\n");
/* Disable color correction */
- regmap_update_bits(backend->regs, SUN4I_BACKEND_OCCTL_REG,
+ regmap_update_bits(engine->regs, SUN4I_BACKEND_OCCTL_REG,
SUN4I_BACKEND_OCCTL_ENABLE, 0);
}
-EXPORT_SYMBOL(sun4i_backend_disable_color_correction);
-void sun4i_backend_commit(struct sun4i_backend *backend)
+static void sun4i_backend_commit(struct sunxi_engine *engine)
{
DRM_DEBUG_DRIVER("Committing changes\n");
- regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG,
+ regmap_write(engine->regs, SUN4I_BACKEND_REGBUFFCTL_REG,
SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS |
SUN4I_BACKEND_REGBUFFCTL_LOADCTL);
}
-EXPORT_SYMBOL(sun4i_backend_commit);
void sun4i_backend_layer_enable(struct sun4i_backend *backend,
int layer, bool enable)
{
u32 val;
- DRM_DEBUG_DRIVER("Enabling layer %d\n", layer);
+ DRM_DEBUG_DRIVER("%sabling layer %d\n", enable ? "En" : "Dis",
+ layer);
if (enable)
val = SUN4I_BACKEND_MODCTL_LAY_EN(layer);
else
val = 0;
- regmap_update_bits(backend->regs, SUN4I_BACKEND_MODCTL_REG,
+ regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,
SUN4I_BACKEND_MODCTL_LAY_EN(layer), val);
}
-EXPORT_SYMBOL(sun4i_backend_layer_enable);
static int sun4i_backend_drm_format_to_layer(struct drm_plane *plane,
u32 format, u32 *mode)
@@ -141,33 +142,33 @@ int sun4i_backend_update_layer_coord(struct sun4i_backend *backend,
if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
state->crtc_w, state->crtc_h);
- regmap_write(backend->regs, SUN4I_BACKEND_DISSIZE_REG,
+ regmap_write(backend->engine.regs, SUN4I_BACKEND_DISSIZE_REG,
SUN4I_BACKEND_DISSIZE(state->crtc_w,
state->crtc_h));
}
/* Set the line width */
DRM_DEBUG_DRIVER("Layer line width: %d bits\n", fb->pitches[0] * 8);
- regmap_write(backend->regs, SUN4I_BACKEND_LAYLINEWIDTH_REG(layer),
+ 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);
- regmap_write(backend->regs, SUN4I_BACKEND_LAYSIZE_REG(layer),
+ regmap_write(backend->engine.regs, SUN4I_BACKEND_LAYSIZE_REG(layer),
SUN4I_BACKEND_LAYSIZE(state->crtc_w,
state->crtc_h));
/* Set base coordinates */
DRM_DEBUG_DRIVER("Layer coordinates X: %d Y: %d\n",
state->crtc_x, state->crtc_y);
- regmap_write(backend->regs, SUN4I_BACKEND_LAYCOOR_REG(layer),
+ regmap_write(backend->engine.regs, SUN4I_BACKEND_LAYCOOR_REG(layer),
SUN4I_BACKEND_LAYCOOR(state->crtc_x,
state->crtc_y));
return 0;
}
-EXPORT_SYMBOL(sun4i_backend_update_layer_coord);
int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
int layer, struct drm_plane *plane)
@@ -182,7 +183,7 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
interlaced = plane->state->crtc->state->adjusted_mode.flags
& DRM_MODE_FLAG_INTERLACE;
- regmap_update_bits(backend->regs, SUN4I_BACKEND_MODCTL_REG,
+ regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,
SUN4I_BACKEND_MODCTL_ITLMOD_EN,
interlaced ? SUN4I_BACKEND_MODCTL_ITLMOD_EN : 0);
@@ -196,12 +197,12 @@ int sun4i_backend_update_layer_formats(struct sun4i_backend *backend,
return ret;
}
- regmap_update_bits(backend->regs, SUN4I_BACKEND_ATTCTL_REG1(layer),
+ regmap_update_bits(backend->engine.regs,
+ SUN4I_BACKEND_ATTCTL_REG1(layer),
SUN4I_BACKEND_ATTCTL_REG1_LAY_FBFMT, val);
return 0;
}
-EXPORT_SYMBOL(sun4i_backend_update_layer_formats);
int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
int layer, struct drm_plane *plane)
@@ -229,19 +230,19 @@ int sun4i_backend_update_layer_buffer(struct sun4i_backend *backend,
/* Write the 32 lower bits of the address (in bits) */
lo_paddr = paddr << 3;
DRM_DEBUG_DRIVER("Setting address lower bits to 0x%x\n", lo_paddr);
- regmap_write(backend->regs, SUN4I_BACKEND_LAYFB_L32ADD_REG(layer),
+ regmap_write(backend->engine.regs,
+ SUN4I_BACKEND_LAYFB_L32ADD_REG(layer),
lo_paddr);
/* And the upper bits */
hi_paddr = paddr >> 29;
DRM_DEBUG_DRIVER("Setting address high bits to 0x%x\n", hi_paddr);
- regmap_update_bits(backend->regs, SUN4I_BACKEND_LAYFB_H4ADD_REG,
+ regmap_update_bits(backend->engine.regs, SUN4I_BACKEND_LAYFB_H4ADD_REG,
SUN4I_BACKEND_LAYFB_H4ADD_MSK(layer),
SUN4I_BACKEND_LAYFB_H4ADD(layer, hi_paddr));
return 0;
}
-EXPORT_SYMBOL(sun4i_backend_update_layer_buffer);
static int sun4i_backend_init_sat(struct device *dev) {
struct sun4i_backend *backend = dev_get_drvdata(dev);
@@ -288,6 +289,52 @@ static int sun4i_backend_free_sat(struct device *dev) {
return 0;
}
+/*
+ * The display backend can take video output from the display frontend, or
+ * the display enhancement unit on the A80, as input for one it its layers.
+ * This relationship within the display pipeline is encoded in the device
+ * tree with of_graph, and we use it here to figure out which backend, if
+ * there are 2 or more, we are currently probing. The number would be in
+ * the "reg" property of the upstream output port endpoint.
+ */
+static int sun4i_backend_of_get_id(struct device_node *node)
+{
+ struct device_node *port, *ep;
+ int ret = -EINVAL;
+
+ /* input is port 0 */
+ port = of_graph_get_port_by_id(node, 0);
+ if (!port)
+ return -EINVAL;
+
+ /* try finding an upstream endpoint */
+ for_each_available_child_of_node(port, ep) {
+ struct device_node *remote;
+ u32 reg;
+
+ remote = of_parse_phandle(ep, "remote-endpoint", 0);
+ if (!remote)
+ continue;
+
+ ret = of_property_read_u32(remote, "reg", &reg);
+ if (ret)
+ continue;
+
+ ret = reg;
+ }
+
+ of_node_put(port);
+
+ return ret;
+}
+
+static const struct sunxi_engine_ops sun4i_backend_engine_ops = {
+ .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,
+};
+
static struct regmap_config sun4i_backend_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
@@ -310,18 +357,23 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
if (!backend)
return -ENOMEM;
dev_set_drvdata(dev, backend);
- drv->backend = backend;
+
+ backend->engine.node = dev->of_node;
+ backend->engine.ops = &sun4i_backend_engine_ops;
+ backend->engine.id = sun4i_backend_of_get_id(dev->of_node);
+ if (backend->engine.id < 0)
+ return backend->engine.id;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs))
return PTR_ERR(regs);
- backend->regs = devm_regmap_init_mmio(dev, regs,
- &sun4i_backend_regmap_config);
- if (IS_ERR(backend->regs)) {
- dev_err(dev, "Couldn't create the backend0 regmap\n");
- return PTR_ERR(backend->regs);
+ backend->engine.regs = devm_regmap_init_mmio(dev, regs,
+ &sun4i_backend_regmap_config);
+ if (IS_ERR(backend->engine.regs)) {
+ dev_err(dev, "Couldn't create the backend regmap\n");
+ return PTR_ERR(backend->engine.regs);
}
backend->reset = devm_reset_control_get(dev, NULL);
@@ -369,16 +421,18 @@ static int sun4i_backend_bind(struct device *dev, struct device *master,
}
}
+ list_add_tail(&backend->engine.list, &drv->engine_list);
+
/* Reset the registers */
for (i = 0x800; i < 0x1000; i += 4)
- regmap_write(backend->regs, i, 0);
+ regmap_write(backend->engine.regs, i, 0);
/* Disable registers autoloading */
- regmap_write(backend->regs, SUN4I_BACKEND_REGBUFFCTL_REG,
+ regmap_write(backend->engine.regs, SUN4I_BACKEND_REGBUFFCTL_REG,
SUN4I_BACKEND_REGBUFFCTL_AUTOLOAD_DIS);
/* Enable the backend */
- regmap_write(backend->regs, SUN4I_BACKEND_MODCTL_REG,
+ regmap_write(backend->engine.regs, SUN4I_BACKEND_MODCTL_REG,
SUN4I_BACKEND_MODCTL_DEBE_EN |
SUN4I_BACKEND_MODCTL_START_CTL);
@@ -400,6 +454,8 @@ static void sun4i_backend_unbind(struct device *dev, struct device *master,
{
struct sun4i_backend *backend = dev_get_drvdata(dev);
+ list_del(&backend->engine.list);
+
if (of_device_is_compatible(dev->of_node,
"allwinner,sun8i-a33-display-backend"))
sun4i_backend_free_sat(dev);