#include "sunxi_engine.h"
 #include "sun4i_tcon.h"
 
+/*
+ * While this isn't really working in the DRM theory, in practice we
+ * can only ever have one encoder per TCON since we have a mux in our
+ * TCON.
+ */
+static struct drm_encoder *sun4i_crtc_get_encoder(struct drm_crtc *crtc)
+{
+       struct drm_encoder *encoder;
+
+       drm_for_each_encoder(encoder, crtc->dev)
+               if (encoder->crtc == crtc)
+                       return encoder;
+
+       return NULL;
+}
+
 static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
                                    struct drm_crtc_state *old_state)
 {
 static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc,
                                      struct drm_crtc_state *old_state)
 {
+       struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc);
        struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
 
        DRM_DEBUG_DRIVER("Disabling the CRTC\n");
 
-       sun4i_tcon_disable(scrtc->tcon);
+       sun4i_tcon_set_status(scrtc->tcon, encoder, false);
 
        if (crtc->state->event && !crtc->state->active) {
                spin_lock_irq(&crtc->dev->event_lock);
 static void sun4i_crtc_atomic_enable(struct drm_crtc *crtc,
                                     struct drm_crtc_state *old_state)
 {
+       struct drm_encoder *encoder = sun4i_crtc_get_encoder(crtc);
        struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
 
        DRM_DEBUG_DRIVER("Enabling the CRTC\n");
 
-       sun4i_tcon_enable(scrtc->tcon);
+       sun4i_tcon_set_status(scrtc->tcon, encoder, true);
 }
 
 static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {
 
 static void sun4i_hdmi_disable(struct drm_encoder *encoder)
 {
        struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
-       struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
-       struct sun4i_tcon *tcon = crtc->tcon;
        u32 val;
 
        DRM_DEBUG_DRIVER("Disabling the HDMI Output\n");
        val = readl(hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
        val &= ~SUN4I_HDMI_VID_CTRL_ENABLE;
        writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
-
-       sun4i_tcon_channel_disable(tcon, 1);
 }
 
 static void sun4i_hdmi_enable(struct drm_encoder *encoder)
 {
        struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
        struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(encoder);
-       struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
-       struct sun4i_tcon *tcon = crtc->tcon;
        u32 val = 0;
 
        DRM_DEBUG_DRIVER("Enabling the HDMI Output\n");
 
-       sun4i_tcon_channel_enable(tcon, 1);
-
        sun4i_hdmi_setup_avi_infoframes(hdmi, mode);
        val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
        val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);
 
 
        DRM_DEBUG_DRIVER("Enabling RGB output\n");
 
-       if (!IS_ERR(tcon->panel))
+       if (!IS_ERR(tcon->panel)) {
                drm_panel_prepare(tcon->panel);
-
-       sun4i_tcon_channel_enable(tcon, 0);
-
-       if (!IS_ERR(tcon->panel))
                drm_panel_enable(tcon->panel);
+       }
 }
 
 static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
 
        DRM_DEBUG_DRIVER("Disabling RGB output\n");
 
-       if (!IS_ERR(tcon->panel))
+       if (!IS_ERR(tcon->panel)) {
                drm_panel_disable(tcon->panel);
-
-       sun4i_tcon_channel_disable(tcon, 0);
-
-       if (!IS_ERR(tcon->panel))
                drm_panel_unprepare(tcon->panel);
+       }
 }
 
 static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder,
 
 #include "sun4i_tcon.h"
 #include "sunxi_engine.h"
 
-void sun4i_tcon_disable(struct sun4i_tcon *tcon)
+static void sun4i_tcon_channel_set_status(struct sun4i_tcon *tcon, int channel,
+                                         bool enabled)
 {
-       DRM_DEBUG_DRIVER("Disabling TCON\n");
+       struct clk *clk;
 
-       /* Disable the TCON */
-       regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
-                          SUN4I_TCON_GCTL_TCON_ENABLE, 0);
-}
-EXPORT_SYMBOL(sun4i_tcon_disable);
-
-void sun4i_tcon_enable(struct sun4i_tcon *tcon)
-{
-       DRM_DEBUG_DRIVER("Enabling TCON\n");
-
-       /* Enable the TCON */
-       regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
-                          SUN4I_TCON_GCTL_TCON_ENABLE,
-                          SUN4I_TCON_GCTL_TCON_ENABLE);
-}
-EXPORT_SYMBOL(sun4i_tcon_enable);
-
-void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel)
-{
-       DRM_DEBUG_DRIVER("Disabling TCON channel %d\n", channel);
-
-       /* Disable the TCON's channel */
-       if (channel == 0) {
+       switch (channel) {
+       case 0:
                regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
-                                  SUN4I_TCON0_CTL_TCON_ENABLE, 0);
-               clk_disable_unprepare(tcon->dclk);
+                                  SUN4I_TCON0_CTL_TCON_ENABLE,
+                                  enabled ? SUN4I_TCON0_CTL_TCON_ENABLE : 0);
+               clk = tcon->dclk;
+               break;
+       case 1:
+               WARN_ON(!tcon->quirks->has_channel_1);
+               regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
+                                  SUN4I_TCON1_CTL_TCON_ENABLE,
+                                  enabled ? SUN4I_TCON1_CTL_TCON_ENABLE : 0);
+               clk = tcon->sclk1;
+               break;
+       default:
+               DRM_WARN("Unknown channel... doing nothing\n");
                return;
        }
 
-       WARN_ON(!tcon->quirks->has_channel_1);
-       regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
-                          SUN4I_TCON1_CTL_TCON_ENABLE, 0);
-       clk_disable_unprepare(tcon->sclk1);
+       if (enabled)
+               clk_prepare_enable(clk);
+       else
+               clk_disable_unprepare(clk);
 }
-EXPORT_SYMBOL(sun4i_tcon_channel_disable);
 
-void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel)
+void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
+                          const struct drm_encoder *encoder,
+                          bool enabled)
 {
-       DRM_DEBUG_DRIVER("Enabling TCON channel %d\n", channel);
+       int channel;
 
-       /* Enable the TCON's channel */
-       if (channel == 0) {
-               regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
-                                  SUN4I_TCON0_CTL_TCON_ENABLE,
-                                  SUN4I_TCON0_CTL_TCON_ENABLE);
-               clk_prepare_enable(tcon->dclk);
+       switch (encoder->encoder_type) {
+       case DRM_MODE_ENCODER_NONE:
+               channel = 0;
+               break;
+       case DRM_MODE_ENCODER_TMDS:
+       case DRM_MODE_ENCODER_TVDAC:
+               channel = 1;
+               break;
+       default:
+               DRM_DEBUG_DRIVER("Unknown encoder type, doing nothing...\n");
                return;
        }
 
-       WARN_ON(!tcon->quirks->has_channel_1);
-       regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG,
-                          SUN4I_TCON1_CTL_TCON_ENABLE,
-                          SUN4I_TCON1_CTL_TCON_ENABLE);
-       clk_prepare_enable(tcon->sclk1);
+       regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
+                          SUN4I_TCON_GCTL_TCON_ENABLE,
+                          enabled ? SUN4I_TCON_GCTL_TCON_ENABLE : 0);
+
+       sun4i_tcon_channel_set_status(tcon, channel, enabled);
 }
-EXPORT_SYMBOL(sun4i_tcon_channel_enable);
 
 void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable)
 {
 
 struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node);
 struct drm_panel *sun4i_tcon_find_panel(struct device_node *node);
 
-/* Global Control */
-void sun4i_tcon_disable(struct sun4i_tcon *tcon);
-void sun4i_tcon_enable(struct sun4i_tcon *tcon);
-
-/* Channel Control */
-void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel);
-void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel);
-
 void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable);
+void sun4i_tcon_set_status(struct sun4i_tcon *crtc,
+                          const struct drm_encoder *encoder, bool enable);
 
 /* Mode Related Controls */
 void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
 
 {
        struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
        struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
-       struct sun4i_tcon *tcon = crtc->tcon;
 
        DRM_DEBUG_DRIVER("Disabling the TV Output\n");
 
-       sun4i_tcon_channel_disable(tcon, 1);
-
        regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
                           SUN4I_TVE_EN_ENABLE,
                           0);
 {
        struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
        struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
-       struct sun4i_tcon *tcon = crtc->tcon;
 
        DRM_DEBUG_DRIVER("Enabling the TV Output\n");
 
        regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
                           SUN4I_TVE_EN_ENABLE,
                           SUN4I_TVE_EN_ENABLE);
-
-       sun4i_tcon_channel_enable(tcon, 1);
 }
 
 static void sun4i_tv_mode_set(struct drm_encoder *encoder,