drm/sun4i: tcon: Don't rely on encoders to enable the TCON

So far, we've required all the TCON-connected encoders to call the TCON
enable and disable functions.

This was made this way because in the RGB/LVDS case, the TCON is the CRTC
and the encoder. However, in all the other cases (HDMI, TV, DSI, etc.), we
have another encoder down the road that needs to be programmed.

We also needed to know which channel the encoder is connected to, which is
encoder-specific.

The CRTC's enable and disable callbacks can work just fine for our use
case, and we can get the channel to use just by looking at the type of
encoder, since that is fixed. Implement those callbacks, which will
remove some of the encoder boilerplate.

Reviewed-by: Chen-Yu Tsai <wens@csie.org>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Link: https://patchwork.freedesktop.org/patch/msgid/90b4396e19b3eca61b2ebfdae0672074b88ad74d.1508231063.git-series.maxime.ripard@free-electrons.com
This commit is contained in:
Maxime Ripard 2017-10-17 11:06:12 +02:00
parent 961c645cf0
commit 45e88f994a
6 changed files with 73 additions and 86 deletions

View File

@ -30,6 +30,22 @@
#include "sunxi_engine.h" #include "sunxi_engine.h"
#include "sun4i_tcon.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, static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *old_state) struct drm_crtc_state *old_state)
{ {
@ -72,11 +88,12 @@ static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc, static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state) 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); struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
DRM_DEBUG_DRIVER("Disabling the CRTC\n"); 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) { if (crtc->state->event && !crtc->state->active) {
spin_lock_irq(&crtc->dev->event_lock); spin_lock_irq(&crtc->dev->event_lock);
@ -90,11 +107,12 @@ static void sun4i_crtc_atomic_disable(struct drm_crtc *crtc,
static void sun4i_crtc_atomic_enable(struct drm_crtc *crtc, static void sun4i_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state) 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); struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
DRM_DEBUG_DRIVER("Enabling the CRTC\n"); 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 const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {

View File

@ -86,8 +86,6 @@ static int sun4i_hdmi_atomic_check(struct drm_encoder *encoder,
static void sun4i_hdmi_disable(struct drm_encoder *encoder) static void sun4i_hdmi_disable(struct drm_encoder *encoder)
{ {
struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(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; u32 val;
DRM_DEBUG_DRIVER("Disabling the HDMI Output\n"); DRM_DEBUG_DRIVER("Disabling the HDMI Output\n");
@ -95,22 +93,16 @@ static void sun4i_hdmi_disable(struct drm_encoder *encoder)
val = readl(hdmi->base + SUN4I_HDMI_VID_CTRL_REG); val = readl(hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
val &= ~SUN4I_HDMI_VID_CTRL_ENABLE; val &= ~SUN4I_HDMI_VID_CTRL_ENABLE;
writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG); writel(val, hdmi->base + SUN4I_HDMI_VID_CTRL_REG);
sun4i_tcon_channel_disable(tcon, 1);
} }
static void sun4i_hdmi_enable(struct drm_encoder *encoder) static void sun4i_hdmi_enable(struct drm_encoder *encoder)
{ {
struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
struct sun4i_hdmi *hdmi = drm_encoder_to_sun4i_hdmi(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 = 0; u32 val = 0;
DRM_DEBUG_DRIVER("Enabling the HDMI Output\n"); DRM_DEBUG_DRIVER("Enabling the HDMI Output\n");
sun4i_tcon_channel_enable(tcon, 1);
sun4i_hdmi_setup_avi_infoframes(hdmi, mode); sun4i_hdmi_setup_avi_infoframes(hdmi, mode);
val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI); val |= SUN4I_HDMI_PKT_CTRL_TYPE(0, SUN4I_HDMI_PKT_AVI);
val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END); val |= SUN4I_HDMI_PKT_CTRL_TYPE(1, SUN4I_HDMI_PKT_END);

View File

@ -134,13 +134,10 @@ static void sun4i_rgb_encoder_enable(struct drm_encoder *encoder)
DRM_DEBUG_DRIVER("Enabling RGB output\n"); DRM_DEBUG_DRIVER("Enabling RGB output\n");
if (!IS_ERR(tcon->panel)) if (!IS_ERR(tcon->panel)) {
drm_panel_prepare(tcon->panel); drm_panel_prepare(tcon->panel);
sun4i_tcon_channel_enable(tcon, 0);
if (!IS_ERR(tcon->panel))
drm_panel_enable(tcon->panel); drm_panel_enable(tcon->panel);
}
} }
static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder) static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
@ -150,13 +147,10 @@ static void sun4i_rgb_encoder_disable(struct drm_encoder *encoder)
DRM_DEBUG_DRIVER("Disabling RGB output\n"); DRM_DEBUG_DRIVER("Disabling RGB output\n");
if (!IS_ERR(tcon->panel)) if (!IS_ERR(tcon->panel)) {
drm_panel_disable(tcon->panel); drm_panel_disable(tcon->panel);
sun4i_tcon_channel_disable(tcon, 0);
if (!IS_ERR(tcon->panel))
drm_panel_unprepare(tcon->panel); drm_panel_unprepare(tcon->panel);
}
} }
static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder, static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder,

View File

@ -35,66 +35,61 @@
#include "sun4i_tcon.h" #include "sun4i_tcon.h"
#include "sunxi_engine.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 */ switch (channel) {
regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG, case 0:
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) {
regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
SUN4I_TCON0_CTL_TCON_ENABLE, 0);
clk_disable_unprepare(tcon->dclk);
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);
}
EXPORT_SYMBOL(sun4i_tcon_channel_disable);
void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel)
{
DRM_DEBUG_DRIVER("Enabling TCON channel %d\n", channel);
/* Enable the TCON's channel */
if (channel == 0) {
regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG, regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
SUN4I_TCON0_CTL_TCON_ENABLE, SUN4I_TCON0_CTL_TCON_ENABLE,
SUN4I_TCON0_CTL_TCON_ENABLE); enabled ? SUN4I_TCON0_CTL_TCON_ENABLE : 0);
clk_prepare_enable(tcon->dclk); 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; return;
} }
WARN_ON(!tcon->quirks->has_channel_1); if (enabled)
regmap_update_bits(tcon->regs, SUN4I_TCON1_CTL_REG, clk_prepare_enable(clk);
SUN4I_TCON1_CTL_TCON_ENABLE, else
SUN4I_TCON1_CTL_TCON_ENABLE); clk_disable_unprepare(clk);
clk_prepare_enable(tcon->sclk1); }
void sun4i_tcon_set_status(struct sun4i_tcon *tcon,
const struct drm_encoder *encoder,
bool enabled)
{
int channel;
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;
}
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) void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable)
{ {

View File

@ -190,15 +190,9 @@ struct sun4i_tcon {
struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node); struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node);
struct drm_panel *sun4i_tcon_find_panel(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_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 */ /* Mode Related Controls */
void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel, void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,

View File

@ -345,12 +345,9 @@ static void sun4i_tv_disable(struct drm_encoder *encoder)
{ {
struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); 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"); DRM_DEBUG_DRIVER("Disabling the TV Output\n");
sun4i_tcon_channel_disable(tcon, 1);
regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
SUN4I_TVE_EN_ENABLE, SUN4I_TVE_EN_ENABLE,
0); 0);
@ -362,7 +359,6 @@ static void sun4i_tv_enable(struct drm_encoder *encoder)
{ {
struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder); struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc); 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"); DRM_DEBUG_DRIVER("Enabling the TV Output\n");
@ -371,8 +367,6 @@ static void sun4i_tv_enable(struct drm_encoder *encoder)
regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG, regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
SUN4I_TVE_EN_ENABLE, SUN4I_TVE_EN_ENABLE,
SUN4I_TVE_EN_ENABLE); SUN4I_TVE_EN_ENABLE);
sun4i_tcon_channel_enable(tcon, 1);
} }
static void sun4i_tv_mode_set(struct drm_encoder *encoder, static void sun4i_tv_mode_set(struct drm_encoder *encoder,