forked from Minki/linux
drm/tegra: dc: Implement atomic DPMS
Move all code into the new canonical ->disable() and ->enable() helper callbacks so that they play extra nice with atomic DPMS. Signed-off-by: Thierry Reding <treding@nvidia.com>
This commit is contained in:
parent
850bab4480
commit
003fc84877
@ -1063,91 +1063,6 @@ static const struct drm_crtc_funcs tegra_crtc_funcs = {
|
||||
.atomic_destroy_state = tegra_crtc_atomic_destroy_state,
|
||||
};
|
||||
|
||||
static void tegra_dc_stop(struct tegra_dc *dc)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
/* stop the display controller */
|
||||
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
|
||||
value &= ~DISP_CTRL_MODE_MASK;
|
||||
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
|
||||
|
||||
tegra_dc_commit(dc);
|
||||
}
|
||||
|
||||
static bool tegra_dc_idle(struct tegra_dc *dc)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND);
|
||||
|
||||
return (value & DISP_CTRL_MODE_MASK) == 0;
|
||||
}
|
||||
|
||||
static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
|
||||
{
|
||||
timeout = jiffies + msecs_to_jiffies(timeout);
|
||||
|
||||
while (time_before(jiffies, timeout)) {
|
||||
if (tegra_dc_idle(dc))
|
||||
return 0;
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
dev_dbg(dc->dev, "timeout waiting for DC to become idle\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void tegra_crtc_disable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct tegra_dc *dc = to_tegra_dc(crtc);
|
||||
u32 value;
|
||||
|
||||
if (!tegra_dc_idle(dc)) {
|
||||
tegra_dc_stop(dc);
|
||||
|
||||
/*
|
||||
* Ignore the return value, there isn't anything useful to do
|
||||
* in case this fails.
|
||||
*/
|
||||
tegra_dc_wait_idle(dc, 100);
|
||||
}
|
||||
|
||||
/*
|
||||
* This should really be part of the RGB encoder driver, but clearing
|
||||
* these bits has the side-effect of stopping the display controller.
|
||||
* When that happens no VBLANK interrupts will be raised. At the same
|
||||
* time the encoder is disabled before the display controller, so the
|
||||
* above code is always going to timeout waiting for the controller
|
||||
* to go idle.
|
||||
*
|
||||
* Given the close coupling between the RGB encoder and the display
|
||||
* controller doing it here is still kind of okay. None of the other
|
||||
* encoder drivers require these bits to be cleared.
|
||||
*
|
||||
* XXX: Perhaps given that the display controller is switched off at
|
||||
* this point anyway maybe clearing these bits isn't even useful for
|
||||
* the RGB encoder?
|
||||
*/
|
||||
if (dc->rgb) {
|
||||
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
|
||||
value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
|
||||
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
|
||||
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
|
||||
}
|
||||
|
||||
tegra_dc_stats_reset(&dc->stats);
|
||||
drm_crtc_vblank_off(crtc);
|
||||
}
|
||||
|
||||
static bool tegra_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static int tegra_dc_set_timings(struct tegra_dc *dc,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
@ -1241,7 +1156,85 @@ static void tegra_dc_commit_state(struct tegra_dc *dc,
|
||||
tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
|
||||
}
|
||||
|
||||
static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
||||
static void tegra_dc_stop(struct tegra_dc *dc)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
/* stop the display controller */
|
||||
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
|
||||
value &= ~DISP_CTRL_MODE_MASK;
|
||||
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
|
||||
|
||||
tegra_dc_commit(dc);
|
||||
}
|
||||
|
||||
static bool tegra_dc_idle(struct tegra_dc *dc)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND);
|
||||
|
||||
return (value & DISP_CTRL_MODE_MASK) == 0;
|
||||
}
|
||||
|
||||
static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
|
||||
{
|
||||
timeout = jiffies + msecs_to_jiffies(timeout);
|
||||
|
||||
while (time_before(jiffies, timeout)) {
|
||||
if (tegra_dc_idle(dc))
|
||||
return 0;
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
}
|
||||
|
||||
dev_dbg(dc->dev, "timeout waiting for DC to become idle\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static void tegra_crtc_disable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct tegra_dc *dc = to_tegra_dc(crtc);
|
||||
u32 value;
|
||||
|
||||
if (!tegra_dc_idle(dc)) {
|
||||
tegra_dc_stop(dc);
|
||||
|
||||
/*
|
||||
* Ignore the return value, there isn't anything useful to do
|
||||
* in case this fails.
|
||||
*/
|
||||
tegra_dc_wait_idle(dc, 100);
|
||||
}
|
||||
|
||||
/*
|
||||
* This should really be part of the RGB encoder driver, but clearing
|
||||
* these bits has the side-effect of stopping the display controller.
|
||||
* When that happens no VBLANK interrupts will be raised. At the same
|
||||
* time the encoder is disabled before the display controller, so the
|
||||
* above code is always going to timeout waiting for the controller
|
||||
* to go idle.
|
||||
*
|
||||
* Given the close coupling between the RGB encoder and the display
|
||||
* controller doing it here is still kind of okay. None of the other
|
||||
* encoder drivers require these bits to be cleared.
|
||||
*
|
||||
* XXX: Perhaps given that the display controller is switched off at
|
||||
* this point anyway maybe clearing these bits isn't even useful for
|
||||
* the RGB encoder?
|
||||
*/
|
||||
if (dc->rgb) {
|
||||
value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
|
||||
value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
|
||||
PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
|
||||
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
|
||||
}
|
||||
|
||||
tegra_dc_stats_reset(&dc->stats);
|
||||
drm_crtc_vblank_off(crtc);
|
||||
}
|
||||
|
||||
static void tegra_crtc_enable(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
|
||||
struct tegra_dc_state *state = to_dc_state(crtc->state);
|
||||
@ -1271,15 +1264,7 @@ static void tegra_crtc_mode_set_nofb(struct drm_crtc *crtc)
|
||||
tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
|
||||
|
||||
tegra_dc_commit(dc);
|
||||
}
|
||||
|
||||
static void tegra_crtc_prepare(struct drm_crtc *crtc)
|
||||
{
|
||||
drm_crtc_vblank_off(crtc);
|
||||
}
|
||||
|
||||
static void tegra_crtc_commit(struct drm_crtc *crtc)
|
||||
{
|
||||
drm_crtc_vblank_on(crtc);
|
||||
}
|
||||
|
||||
@ -1314,10 +1299,7 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc)
|
||||
|
||||
static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
|
||||
.disable = tegra_crtc_disable,
|
||||
.mode_fixup = tegra_crtc_mode_fixup,
|
||||
.mode_set_nofb = tegra_crtc_mode_set_nofb,
|
||||
.prepare = tegra_crtc_prepare,
|
||||
.commit = tegra_crtc_commit,
|
||||
.enable = tegra_crtc_enable,
|
||||
.atomic_check = tegra_crtc_atomic_check,
|
||||
.atomic_begin = tegra_crtc_atomic_begin,
|
||||
.atomic_flush = tegra_crtc_atomic_flush,
|
||||
@ -1368,6 +1350,14 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data)
|
||||
{
|
||||
struct drm_info_node *node = s->private;
|
||||
struct tegra_dc *dc = node->info_ent->data;
|
||||
int err = 0;
|
||||
|
||||
drm_modeset_lock_crtc(&dc->base, NULL);
|
||||
|
||||
if (!dc->base.state->active) {
|
||||
err = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
#define DUMP_REG(name) \
|
||||
seq_printf(s, "%-40s %#05x %08x\n", #name, name, \
|
||||
@ -1588,15 +1578,25 @@ static int tegra_dc_show_regs(struct seq_file *s, void *data)
|
||||
|
||||
#undef DUMP_REG
|
||||
|
||||
return 0;
|
||||
unlock:
|
||||
drm_modeset_unlock_crtc(&dc->base);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra_dc_show_crc(struct seq_file *s, void *data)
|
||||
{
|
||||
struct drm_info_node *node = s->private;
|
||||
struct tegra_dc *dc = node->info_ent->data;
|
||||
int err = 0;
|
||||
u32 value;
|
||||
|
||||
drm_modeset_lock_crtc(&dc->base, NULL);
|
||||
|
||||
if (!dc->base.state->active) {
|
||||
err = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE;
|
||||
tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL);
|
||||
tegra_dc_commit(dc);
|
||||
@ -1609,7 +1609,9 @@ static int tegra_dc_show_crc(struct seq_file *s, void *data)
|
||||
|
||||
tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL);
|
||||
|
||||
return 0;
|
||||
unlock:
|
||||
drm_modeset_unlock_crtc(&dc->base);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tegra_dc_show_stats(struct seq_file *s, void *data)
|
||||
|
Loading…
Reference in New Issue
Block a user