summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/sun4i/sun4i_tcon.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/sun4i/sun4i_tcon.c')
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tcon.c91
1 files changed, 74 insertions, 17 deletions
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index e853dfe51389..68751b999877 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -14,9 +14,12 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_modes.h>
#include <drm/drm_of.h>
+#include <uapi/drm/drm_mode.h>
+
#include <linux/component.h>
#include <linux/ioport.h>
#include <linux/of_address.h>
@@ -109,26 +112,37 @@ void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable)
}
EXPORT_SYMBOL(sun4i_tcon_enable_vblank);
-void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
- struct drm_encoder *encoder)
+/*
+ * This function is a helper for TCON output muxing. The TCON output
+ * muxing control register in earlier SoCs (without the TCON TOP block)
+ * are located in TCON0. This helper returns a pointer to TCON0's
+ * sun4i_tcon structure, or NULL if not found.
+ */
+static struct sun4i_tcon *sun4i_get_tcon0(struct drm_device *drm)
{
- u32 val;
+ struct sun4i_drv *drv = drm->dev_private;
+ struct sun4i_tcon *tcon;
- if (!tcon->quirks->has_unknown_mux)
- return;
+ list_for_each_entry(tcon, &drv->tcon_list, list)
+ if (tcon->id == 0)
+ return tcon;
- if (channel != 1)
- return;
+ dev_warn(drm->dev,
+ "TCON0 not found, display output muxing may not work\n");
- if (encoder->encoder_type == DRM_MODE_ENCODER_TVDAC)
- val = 1;
- else
- val = 0;
+ return NULL;
+}
- /*
- * FIXME: Undocumented bits
- */
- regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, val);
+void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
+ struct drm_encoder *encoder)
+{
+ int ret = -ENOTSUPP;
+
+ if (tcon->quirks->set_mux)
+ ret = tcon->quirks->set_mux(tcon, encoder);
+
+ DRM_DEBUG_DRIVER("Muxing encoder %s to CRTC %s: %d\n",
+ encoder->name, encoder->crtc->name, ret);
}
EXPORT_SYMBOL(sun4i_tcon_set_mux);
@@ -767,14 +781,57 @@ static int sun4i_tcon_remove(struct platform_device *pdev)
return 0;
}
+/* platform specific TCON muxing callbacks */
+static int sun5i_a13_tcon_set_mux(struct sun4i_tcon *tcon,
+ struct drm_encoder *encoder)
+{
+ u32 val;
+
+ if (encoder->encoder_type == DRM_MODE_ENCODER_TVDAC)
+ val = 1;
+ else
+ val = 0;
+
+ /*
+ * FIXME: Undocumented bits
+ */
+ return regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, val);
+}
+
+static int sun6i_tcon_set_mux(struct sun4i_tcon *tcon,
+ struct drm_encoder *encoder)
+{
+ struct sun4i_tcon *tcon0 = sun4i_get_tcon0(encoder->dev);
+ u32 shift;
+
+ if (!tcon0)
+ return -EINVAL;
+
+ switch (encoder->encoder_type) {
+ case DRM_MODE_ENCODER_TMDS:
+ /* HDMI */
+ shift = 8;
+ break;
+ default:
+ /* TODO A31 has MIPI DSI but A31s does not */
+ return -EINVAL;
+ }
+
+ regmap_update_bits(tcon0->regs, SUN4I_TCON_MUX_CTRL_REG,
+ 0x3 << shift, tcon->id << shift);
+
+ return 0;
+}
+
static const struct sun4i_tcon_quirks sun5i_a13_quirks = {
- .has_unknown_mux = true,
- .has_channel_1 = true,
+ .has_channel_1 = true,
+ .set_mux = sun5i_a13_tcon_set_mux,
};
static const struct sun4i_tcon_quirks sun6i_a31_quirks = {
.has_channel_1 = true,
.needs_de_be_mux = true,
+ .set_mux = sun6i_tcon_set_mux,
};
static const struct sun4i_tcon_quirks sun6i_a31s_quirks = {