summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPixelBot AutoMerger <android-nexus-securitybot@system.gserviceaccount.com>2023-02-05 19:47:23 -0800
committerSecurityBot <android-nexus-securitybot@system.gserviceaccount.com>2023-02-05 19:47:23 -0800
commit953c06fdde8ec60a2e3bfc7f35c6aa420a4d8f36 (patch)
treedfea7978e556bdcaedcc2ece674bfc644321c334
parent66bc56f8a4113835374808b1494731107484bd01 (diff)
parent5a98b191812677d6b3f041f498ecc74a30eb58fd (diff)
downloaddisplay-953c06fdde8ec60a2e3bfc7f35c6aa420a4d8f36.tar.gz
Merge android13-gs-pixel-5.10-tm-qpr3 into android13-gs-pixel-5.10-udc
SBMerger: 478053055 Change-Id: I6425bc3500fd01a2d70676348c2c8b72b9f9f6de Signed-off-by: SecurityBot <android-nexus-securitybot@system.gserviceaccount.com>
-rw-r--r--samsung/exynos_drm_bts.c35
-rw-r--r--samsung/exynos_drm_crtc.c83
-rw-r--r--samsung/exynos_drm_crtc.h6
-rw-r--r--samsung/exynos_drm_debug.c19
-rw-r--r--samsung/exynos_drm_decon.c139
-rw-r--r--samsung/exynos_drm_decon.h13
-rw-r--r--samsung/exynos_drm_drv.c11
-rw-r--r--samsung/exynos_drm_fb.c161
8 files changed, 400 insertions, 67 deletions
diff --git a/samsung/exynos_drm_bts.c b/samsung/exynos_drm_bts.c
index f47c4b2..7fc453a 100644
--- a/samsung/exynos_drm_bts.c
+++ b/samsung/exynos_drm_bts.c
@@ -343,7 +343,7 @@ static u64 dpu_bts_calc_aclk_disp(struct decon_device *decon,
const struct dpu_bts_win_config *config, u64 resol_clk,
u32 max_clk)
{
- u64 aclk_disp, aclk_base, aclk_disp_khz;
+ u64 aclk_disp, aclk_base, aclk_panel_khz, aclk_disp_khz;
u32 ppc;
u32 src_w, src_h;
u32 diff_w, ratio_v;
@@ -362,12 +362,16 @@ static u64 dpu_bts_calc_aclk_disp(struct decon_device *decon,
if (src_w > config->dst_w || src_h > config->dst_h)
is_downscale = true;
- /* case for using dsc encoder 1ea at decon0 or decon1 */
- if ((decon->id != 2) && (decon->config.dsc.dsc_count == 1))
- ppc = ((decon->bts.ppc / 2UL) >= 1UL) ?
- (decon->bts.ppc / 2UL) : 1UL;
- else
+ /* when calculating aclk for panel, if DSC is enabled, consider DSC encoder
+ * count as its ppc.
+ */
+ if (decon->config.dsc.enabled) {
+ ppc = min(decon->bts.ppc, decon->config.dsc.dsc_count);
+ is_dsc = true;
+ } else {
ppc = decon->bts.ppc;
+ }
+ aclk_panel_khz = resol_clk / ppc;
margin = 1100 + ((48000 + 20000) / decon->config.image_width);
diff_w = (src_w <= config->dst_w) ? 0 : src_w - config->dst_w;
@@ -389,10 +393,10 @@ static u64 dpu_bts_calc_aclk_disp(struct decon_device *decon,
}
aclk_disp = mult_frac(aclk_disp, decon->config.image_height * decon->bts.fps, 1000);
aclk_disp_khz = (aclk_disp * margin / 1000) / 1000;
+ aclk_disp_khz /= decon->bts.ppc;
- if (aclk_disp_khz < resol_clk)
- aclk_disp_khz = resol_clk;
- aclk_disp_khz /= ppc;
+ if (aclk_disp_khz < aclk_panel_khz)
+ aclk_disp_khz = aclk_panel_khz;
if (!config->is_rot)
return aclk_disp_khz;
@@ -403,9 +407,6 @@ static u64 dpu_bts_calc_aclk_disp(struct decon_device *decon,
else
aclk_base = max_clk;
- if (decon->config.dsc.enabled)
- is_dsc = true;
-
aclk_disp_khz = dpu_bts_calc_rotate_aclk(decon, (u32)aclk_base, ppc,
src_w, config->dst_w, config->is_comp, is_downscale, is_dsc);
@@ -881,6 +882,16 @@ static void dpu_bts_update_resources(struct decon_device *decon, bool shadow_upd
DPU_DEBUG_BTS(" peak = %u, rt = %u, read = %u, write = %u\n",
bw.peak, bw.rt, bw.read, bw.write);
+ /* When concurrent writeback is enabled, writeback instant off may occur if the outfifo
+ * for writeback is full meanwhile the outfifo for LCD is not full yet.
+ * We can limit max_disp_freq to avoid it when concurrent writeback is enabled.
+ * Currently, the issue only occurs when all layers are solid color layers (read = 0).
+ */
+ if ((decon->bts.max_dfs_lv_for_wb > 0) && (bw.read == 0) && (bw.write > 0)) {
+ decon->bts.max_disp_freq =
+ min(decon->bts.max_disp_freq, decon->bts.max_dfs_lv_for_wb);
+ }
+
if (shadow_updated) {
/* after DECON h/w configs are updated to shadow SFR */
if (decon->bts.total_bw < decon->bts.prev_total_bw ||
diff --git a/samsung/exynos_drm_crtc.c b/samsung/exynos_drm_crtc.c
index eff9eb8..68caafe 100644
--- a/samsung/exynos_drm_crtc.c
+++ b/samsung/exynos_drm_crtc.c
@@ -15,6 +15,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
#include <drm/drm_encoder.h>
#include <drm/drm_color_mgmt.h>
#include <drm/drm_crtc_helper.h>
@@ -1050,3 +1051,85 @@ void exynos_crtc_wait_for_flip_done(struct drm_atomic_state *old_state)
old_crtc_state, new_crtc_state);
}
}
+
+bool exynos_crtc_needs_disable(struct drm_crtc_state *old_state,
+ struct drm_crtc_state *new_state)
+{
+ /*
+ * No new_state means the CRTC is off, so the only criteria is whether
+ * it's currently active or in self refresh mode.
+ */
+ if (!new_state)
+ return drm_atomic_crtc_effectively_active(old_state);
+
+ /*
+ * We need to run through the crtc_funcs->disable() function if the CRTC
+ * is currently on, if it's transitioning to self refresh mode, or if
+ * it's in self refresh mode and needs to be fully disabled.
+ */
+ return old_state->active ||
+ (old_state->self_refresh_active && !new_state->enable) ||
+ new_state->self_refresh_active;
+}
+
+void exynos_crtc_set_mode(struct drm_device *dev,
+ struct drm_atomic_state *old_state)
+{
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *new_crtc_state;
+ struct drm_connector *connector;
+ struct drm_connector_state *new_conn_state;
+ int i;
+
+ for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
+ const struct drm_crtc_helper_funcs *funcs;
+
+ if (!new_crtc_state->mode_changed)
+ continue;
+
+ funcs = crtc->helper_private;
+
+ if (new_crtc_state->enable && funcs->mode_set_nofb) {
+ DRM_DEBUG_ATOMIC("modeset on [CRTC:%d:%s]\n",
+ crtc->base.id, crtc->name);
+
+ funcs->mode_set_nofb(crtc);
+ }
+ }
+
+ for_each_new_connector_in_state(old_state, connector, new_conn_state, i) {
+ const struct drm_encoder_helper_funcs *funcs;
+ struct drm_encoder *encoder;
+ struct drm_display_mode *mode, *adjusted_mode;
+ struct drm_bridge *bridge;
+
+ if (!new_conn_state->best_encoder)
+ continue;
+
+ encoder = new_conn_state->best_encoder;
+ funcs = encoder->helper_private;
+ new_crtc_state = new_conn_state->crtc->state;
+ mode = &new_crtc_state->mode;
+ adjusted_mode = &new_crtc_state->adjusted_mode;
+
+ if (!new_crtc_state->mode_changed)
+ continue;
+
+ DRM_DEBUG_ATOMIC("modeset on [ENCODER:%d:%s]\n",
+ encoder->base.id, encoder->name);
+
+ /*
+ * Each encoder has at most one connector (since we always steal
+ * it away), so we won't call mode_set hooks twice.
+ */
+ if (funcs && funcs->atomic_mode_set) {
+ funcs->atomic_mode_set(encoder, new_crtc_state,
+ new_conn_state);
+ } else if (funcs && funcs->mode_set) {
+ funcs->mode_set(encoder, mode, adjusted_mode);
+ }
+
+ bridge = drm_bridge_chain_get_first_bridge(encoder);
+ drm_bridge_chain_mode_set(bridge, mode, adjusted_mode);
+ }
+}
diff --git a/samsung/exynos_drm_crtc.h b/samsung/exynos_drm_crtc.h
index 7d13e92..42d4100 100644
--- a/samsung/exynos_drm_crtc.h
+++ b/samsung/exynos_drm_crtc.h
@@ -59,4 +59,10 @@ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc);
void exynos_crtc_handle_event(struct exynos_drm_crtc *exynos_crtc);
void exynos_crtc_wait_for_flip_done(struct drm_atomic_state *old_state);
+
+bool exynos_crtc_needs_disable(struct drm_crtc_state *old_state,
+ struct drm_crtc_state *new_state);
+
+void exynos_crtc_set_mode(struct drm_device *dev,
+ struct drm_atomic_state *old_state);
#endif
diff --git a/samsung/exynos_drm_debug.c b/samsung/exynos_drm_debug.c
index afdd18d..7c5487f 100644
--- a/samsung/exynos_drm_debug.c
+++ b/samsung/exynos_drm_debug.c
@@ -212,6 +212,15 @@ void DPU_EVENT_LOG(enum dpu_event_type type, int index, void *priv)
log->data.pd.decon_state = decon->state;
log->data.pd.rpm_active = pm_runtime_active(decon->dev);
break;
+ case DPU_EVT_DECON_UPDATE_CONFIG:
+ log->data.decon_cfg.fps = decon->bts.fps;
+ log->data.decon_cfg.image_height = decon->config.image_height;
+ log->data.decon_cfg.image_width = decon->config.image_width;
+ log->data.decon_cfg.out_type = decon->config.out_type;
+ log->data.decon_cfg.mode.op_mode = decon->config.mode.op_mode;
+ log->data.decon_cfg.mode.dsi_mode = decon->config.mode.dsi_mode;
+ log->data.decon_cfg.mode.trig_mode = decon->config.mode.trig_mode;
+ break;
case DPU_EVT_DSIM_RUNTIME_SUSPEND:
case DPU_EVT_DSIM_RUNTIME_RESUME:
case DPU_EVT_DSIM_SUSPEND:
@@ -520,6 +529,7 @@ static const char *get_event_name(enum dpu_event_type type)
"DECON_FRAMESTART",
"DECON_RSC_OCCUPANCY",
"DECON_TRIG_MASK",
+ "DECON_UPDATE_CONFIG",
"DSIM_ENABLED",
"DSIM_DISABLED",
"DSIM_COMMAND",
@@ -810,6 +820,15 @@ static void dpu_event_log_print(const struct decon_device *decon, struct drm_pri
log->data.pd.rpm_active ? "ON" : "OFF",
log->data.pd.decon_state);
break;
+ case DPU_EVT_DECON_UPDATE_CONFIG:
+ scnprintf(buf + len, sizeof(buf) - len,
+ "\t%s mode, %s_trigger, out type:0x%x, dsi_mode:%d.(%dx%d@%dhz)",
+ log->data.decon_cfg.mode.op_mode ? "command" : "video",
+ log->data.decon_cfg.mode.trig_mode ? "sw" : "hw",
+ log->data.decon_cfg.out_type, log->data.decon_cfg.mode.dsi_mode,
+ log->data.decon_cfg.image_width, log->data.decon_cfg.image_height,
+ log->data.decon_cfg.fps);
+ break;
case DPU_EVT_DSIM_RUNTIME_SUSPEND:
case DPU_EVT_DSIM_RUNTIME_RESUME:
case DPU_EVT_DSIM_SUSPEND:
diff --git a/samsung/exynos_drm_decon.c b/samsung/exynos_drm_decon.c
index 2169c30..c772375 100644
--- a/samsung/exynos_drm_decon.c
+++ b/samsung/exynos_drm_decon.c
@@ -161,6 +161,11 @@ static void decon_set_color_map(struct decon_device *decon, u32 win_id,
decon_debug(decon, "%s -\n", __func__);
}
+static inline bool decon_is_effectively_active(const struct decon_device *decon)
+{
+ return decon->state == DECON_STATE_ON || decon->state == DECON_STATE_HIBERNATION;
+}
+
static inline bool decon_is_te_enabled(const struct decon_device *decon)
{
return (decon->config.mode.op_mode == DECON_COMMAND_MODE) &&
@@ -337,6 +342,7 @@ static void decon_update_config(struct decon_config *config,
const struct exynos_drm_connector_state *exynos_conn_state)
{
const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+ struct decon_device *decon = crtc_to_decon(crtc_state->crtc);
config->image_width = mode->hdisplay;
config->image_height = mode->vdisplay;
@@ -369,6 +375,8 @@ static void decon_update_config(struct decon_config *config,
decon_update_dsi_config(config, crtc_state, exynos_conn_state);
config->out_bpc = exynos_conn_state->exynos_mode.bpc;
+
+ DPU_EVENT_LOG(DPU_EVT_DECON_UPDATE_CONFIG, decon->id, NULL);
}
static bool decon_is_seamless_possible(const struct decon_device *decon,
@@ -463,6 +471,7 @@ static int _decon_handover_check(struct exynos_drm_crtc *exynos_crtc,
dpp->state = DPP_STATE_HANDOVER;
dpp->win_id = i;
dpp->decon_id = decon->id;
+ dpp->is_win_connected = true;
found_handover_dpp = true;
}
}
@@ -589,12 +598,9 @@ static void decon_disable_win(struct decon_device *decon, int win_id)
static void _dpp_disable(struct dpp_device *dpp)
{
- if (dpp->is_win_connected) {
+ if (dpp->disable)
dpp->disable(dpp);
- dpp->is_win_connected = false;
- } else if (test_bit(DPP_ATTR_RCD, &dpp->attr)) {
- dpp->disable(dpp);
- }
+ dpp->is_win_connected = false;
}
static void decon_update_plane(struct exynos_drm_crtc *exynos_crtc,
@@ -948,9 +954,8 @@ static void decon_enable_irqs(struct decon_device *decon)
enable_irq(decon->irq_de);
}
-static void _decon_enable(struct decon_device *decon)
+static void _decon_enable_locked(struct decon_device *decon)
{
- decon->state = DECON_STATE_ON;
decon_reg_init(decon->id, &decon->config);
decon_enable_irqs(decon);
}
@@ -1118,7 +1123,7 @@ static void decon_seamless_mode_set(struct exynos_drm_crtc *exynos_crtc,
}
}
-static int _decon_reinit(struct decon_device *decon)
+static int _decon_reinit_locked(struct decon_device *decon)
{
int i;
@@ -1148,7 +1153,7 @@ static int _decon_reinit(struct decon_device *decon)
return 0;
}
-static void _decon_stop(struct decon_device *decon, bool reset, u32 vrefresh)
+static void _decon_stop_locked(struct decon_device *decon, bool reset, u32 vrefresh)
{
int i;
const u32 fps = min(decon->bts.fps, vrefresh) ? : 60;
@@ -1165,7 +1170,7 @@ static void _decon_stop(struct decon_device *decon, bool reset, u32 vrefresh)
decon->bts.rcd_win_config.win.state = DPU_WIN_STATE_DISABLED;
decon->bts.rcd_win_config.dma_addr = 0;
- _decon_reinit(decon);
+ _decon_reinit_locked(decon);
decon_reg_stop(decon->id, &decon->config, reset, fps);
@@ -1175,6 +1180,8 @@ static void _decon_stop(struct decon_device *decon, bool reset, u32 vrefresh)
static void decon_exit_hibernation(struct decon_device *decon)
{
+ unsigned long flags;
+
if (decon->state != DECON_STATE_HIBERNATION)
return;
@@ -1182,13 +1189,16 @@ static void decon_exit_hibernation(struct decon_device *decon)
DPU_ATRACE_BEGIN(__func__);
decon_debug(decon, "%s +\n", __func__);
- pm_runtime_get_sync(decon->dev);
- _decon_enable(decon);
+ if (pm_runtime_get_sync(decon->dev) < 0)
+ decon_err(decon, "%s: failed to pm_runtime_get_sync\n", __func__);
+ spin_lock_irqsave(&decon->slock, flags);
+ _decon_enable_locked(decon);
exynos_dqe_restore_lpd_data(decon->dqe);
-
if (decon->partial)
exynos_partial_restore(decon->partial);
+ decon->state = DECON_STATE_ON;
+ spin_unlock_irqrestore(&decon->slock, flags);
decon_debug(decon, "%s -\n", __func__);
DPU_ATRACE_END(__func__);
@@ -1217,6 +1227,7 @@ static void decon_enable(struct exynos_drm_crtc *exynos_crtc, struct drm_crtc_st
struct exynos_drm_crtc_state *old_exynos_crtc_state = to_exynos_crtc_state(old_crtc_state);
struct decon_device *decon = exynos_crtc->ctx;
int vrefresh = drm_mode_vrefresh(&old_crtc_state->mode);
+ unsigned long flags;
if (decon->state == DECON_STATE_ON) {
decon_info(decon, "already enabled(%d)\n", decon->state);
@@ -1228,8 +1239,11 @@ static void decon_enable(struct exynos_drm_crtc *exynos_crtc, struct drm_crtc_st
if (decon->state == DECON_STATE_HIBERNATION) {
WARN_ON(!old_crtc_state->self_refresh_active);
- if (old_exynos_crtc_state->bypass)
- _decon_stop(decon, true, vrefresh);
+ if (old_exynos_crtc_state->bypass) {
+ spin_lock_irqsave(&decon->slock, flags);
+ _decon_stop_locked(decon, true, vrefresh);
+ spin_unlock_irqrestore(&decon->slock, flags);
+ }
decon_exit_hibernation(decon);
goto ret;
@@ -1250,15 +1264,17 @@ static void decon_enable(struct exynos_drm_crtc *exynos_crtc, struct drm_crtc_st
pm_runtime_get_sync(decon->dev);
+ spin_lock_irqsave(&decon->slock, flags);
if (decon->state == DECON_STATE_HANDOVER) {
- _decon_reinit(decon);
+ _decon_reinit_locked(decon);
/* remove pm_runtime ref taken during probe */
- pm_runtime_put_sync(decon->dev);
+ pm_runtime_put(decon->dev);
} else if (decon->state == DECON_STATE_INIT) {
- _decon_stop(decon, true, drm_mode_vrefresh(&old_crtc_state->mode));
+ _decon_stop_locked(decon, true, drm_mode_vrefresh(&old_crtc_state->mode));
}
-
- _decon_enable(decon);
+ _decon_enable_locked(decon);
+ decon->state = DECON_STATE_ON;
+ spin_unlock_irqrestore(&decon->slock, flags);
decon_print_config_info(decon);
@@ -1299,24 +1315,28 @@ ret:
static void decon_disable_irqs(struct decon_device *decon)
{
- disable_irq(decon->irq_fd);
- disable_irq(decon->irq_ext);
+ disable_irq_nosync(decon->irq_fd);
+ disable_irq_nosync(decon->irq_ext);
if (decon->irq_ds >= 0)
- disable_irq(decon->irq_ds);
+ disable_irq_nosync(decon->irq_ds);
if (decon->irq_de >= 0)
- disable_irq(decon->irq_de);
+ disable_irq_nosync(decon->irq_de);
decon_reg_set_interrupts(decon->id, 0);
if (decon_is_te_enabled(decon))
- disable_irq(decon->irq_fs);
+ disable_irq_nosync(decon->irq_fs);
}
-static void _decon_disable(struct decon_device *decon)
+static u32 _decon_get_current_fps(struct decon_device *decon)
{
struct drm_crtc *crtc = &decon->crtc->base;
const struct drm_crtc_state *crtc_state = crtc->state;
- bool reset = drm_atomic_crtc_needs_modeset(crtc_state);
- const u32 fps = min_t(u32, decon->bts.fps,
- drm_mode_vrefresh(&crtc_state->mode)) ? : 60;
+
+ return min_t(u32, decon->bts.fps, drm_mode_vrefresh(&crtc_state->mode)) ?: 60;
+}
+
+static bool _decon_wait_for_framedone(struct decon_device *decon)
+{
+ const u32 fps = _decon_get_current_fps(decon);
const u64 timeout = fps_timeout(fps);
u64 ret;
@@ -1326,20 +1346,30 @@ static void _decon_disable(struct decon_device *decon)
timeout);
if (!ret) {
WARN(1, "decon%d: wait for frame done timed out (%dhz)", decon->id, fps);
- reset = true;
+ return true;
} else {
+ struct drm_crtc *crtc = &decon->crtc->base;
+ const struct drm_crtc_state *crtc_state = crtc->state;
+ bool reset = drm_atomic_crtc_needs_modeset(crtc_state);
+
decon_debug(decon, "%s: frame done after: ~%dus (%dhz)", __func__,
jiffies_to_usecs(timeout - ret), fps);
+ return reset;
}
+}
+static void _decon_disable_locked(struct decon_device *decon, bool reset)
+{
decon_disable_irqs(decon);
atomic_set(&decon->frames_pending, 0);
- _decon_stop(decon, reset, fps);
- decon->state = DECON_STATE_HIBERNATION;
+ _decon_stop_locked(decon, reset, _decon_get_current_fps(decon));
}
static void decon_enter_hibernation(struct decon_device *decon)
{
+ bool reset = false;
+ unsigned long flags;
+
if (decon->state != DECON_STATE_ON)
return;
@@ -1348,8 +1378,12 @@ static void decon_enter_hibernation(struct decon_device *decon)
DPU_ATRACE_BEGIN(__func__);
DPU_EVENT_LOG(DPU_EVT_ENTER_HIBERNATION_IN, decon->id, NULL);
- _decon_disable(decon);
- pm_runtime_put_sync(decon->dev);
+ reset = _decon_wait_for_framedone(decon);
+ spin_lock_irqsave(&decon->slock, flags);
+ _decon_disable_locked(decon, reset);
+ pm_runtime_put(decon->dev);
+ decon->state = DECON_STATE_HIBERNATION;
+ spin_unlock_irqrestore(&decon->slock, flags);
DPU_EVENT_LOG(DPU_EVT_ENTER_HIBERNATION_OUT, decon->id, NULL);
DPU_ATRACE_END(__func__);
@@ -1363,6 +1397,8 @@ static void decon_disable(struct exynos_drm_crtc *crtc)
struct drm_crtc_state *crtc_state = crtc->base.state;
struct exynos_drm_crtc_state *exynos_crtc_state = to_exynos_crtc_state(crtc_state);
const enum decon_state old_decon_state = decon->state;
+ bool reset;
+ unsigned long flags;
if (old_decon_state == DECON_STATE_OFF)
return;
@@ -1379,9 +1415,6 @@ static void decon_disable(struct exynos_drm_crtc *crtc)
decon_info(decon, "%s +\n", __func__);
- if (old_decon_state == DECON_STATE_ON)
- _decon_disable(decon);
-
if (crtc_state->mode_changed || crtc_state->connectors_changed) {
if (decon->irq_te >= 0) {
if (atomic_read(&decon->te_ref))
@@ -1391,9 +1424,14 @@ static void decon_disable(struct exynos_drm_crtc *crtc)
}
}
+ reset = _decon_wait_for_framedone(decon);
+ spin_lock_irqsave(&decon->slock, flags);
+ if (old_decon_state == DECON_STATE_ON) {
+ _decon_disable_locked(decon, reset);
+ pm_runtime_put(decon->dev);
+ }
decon->state = DECON_STATE_OFF;
- if (old_decon_state == DECON_STATE_ON)
- pm_runtime_put_sync(decon->dev);
+ spin_unlock_irqrestore(&decon->slock, flags);
DPU_EVENT_LOG(DPU_EVT_DECON_DISABLED, decon->id, decon);
@@ -1606,8 +1644,10 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_data)
u32 ext_irq = 0;
spin_lock(&decon->slock);
- if (decon->state != DECON_STATE_ON)
+ if (decon->state != DECON_STATE_ON) {
+ decon_warn(decon, "%s: irq occurs with decon->state=%d\n", __func__, decon->state);
goto irq_end;
+ }
irq_sts_reg = decon_reg_get_interrupt_and_clear(decon->id, &ext_irq);
decon_debug(decon, "%s: irq_sts_reg = %x, ext_irq = %x\n",
@@ -1769,8 +1809,8 @@ static int decon_parse_dt(struct decon_device *decon, struct device_node *np)
}
if (of_property_read_u32(np, "ppc", (u32 *)&decon->bts.ppc))
- decon->bts.ppc = 2UL;
- decon_info(decon, "PPC(%llu)\n", decon->bts.ppc);
+ decon->bts.ppc = 2U;
+ decon_info(decon, "PPC(%u)\n", decon->bts.ppc);
if (of_property_read_u32(np, "ppc_rotator",
(u32 *)&decon->bts.ppc_rotator)) {
@@ -1855,6 +1895,13 @@ static int decon_parse_dt(struct decon_device *decon, struct device_node *np)
decon_info(decon, "\n");
}
+ if (of_property_read_u32(np, "max_dfs_lv_for_wb", &decon->bts.max_dfs_lv_for_wb)) {
+ decon->bts.max_dfs_lv_for_wb = 0;
+ decon_debug(decon, "max_dfs_lv_for_wb is not defined in DT.\n");
+ } else {
+ decon_debug(decon, "max_dfs_lv_for_wb(%u)\n", decon->bts.max_dfs_lv_for_wb);
+ }
+
decon->dpp_cnt = of_count_phandle_with_args(np, "dpps", NULL);
for (i = 0; i < decon->dpp_cnt; ++i) {
dpp_np = of_parse_phandle(np, "dpps", i);
@@ -2247,7 +2294,10 @@ static int decon_runtime_resume(struct device *dev)
static int decon_suspend(struct device *dev)
{
struct decon_device *decon = dev_get_drvdata(dev);
- int ret = 0;
+ int ret;
+
+ if (!decon_is_effectively_active(decon))
+ return 0;
decon_debug(decon, "%s\n", __func__);
@@ -2262,6 +2312,9 @@ static int decon_resume(struct device *dev)
{
struct decon_device *decon = dev_get_drvdata(dev);
+ if (!decon_is_effectively_active(decon))
+ return 0;
+
decon_debug(decon, "%s\n", __func__);
DPU_EVENT_LOG(DPU_EVT_DECON_RESUME, decon->id, NULL);
diff --git a/samsung/exynos_drm_decon.h b/samsung/exynos_drm_decon.h
index 31a487a..1d5ce3f 100644
--- a/samsung/exynos_drm_decon.h
+++ b/samsung/exynos_drm_decon.h
@@ -141,7 +141,7 @@ struct dpu_bts {
u32 max_disp_freq;
u32 prev_max_disp_freq;
u32 dvfs_max_disp_freq;
- u64 ppc;
+ u32 ppc;
u32 ppc_rotator;
u32 ppc_scaler;
u32 delay_comp;
@@ -155,6 +155,7 @@ struct dpu_bts {
u32 afbc_yuv_rt_util_pct;
u32 dfs_lv_cnt;
u32 dfs_lv_khz[BTS_DFS_MAX];
+ u32 max_dfs_lv_for_wb;
u32 vbp;
u32 vfp;
u32 vsa;
@@ -194,6 +195,7 @@ enum dpu_event_type {
DPU_EVT_DECON_FRAMESTART,
DPU_EVT_DECON_RSC_OCCUPANCY,
DPU_EVT_DECON_TRIG_MASK,
+ DPU_EVT_DECON_UPDATE_CONFIG,
DPU_EVT_DSIM_ENABLED,
DPU_EVT_DSIM_DISABLED,
@@ -381,6 +383,14 @@ struct dpu_log_plane_info {
u32 format;
};
+struct dpu_log_decon_cfg {
+ u32 fps;
+ u32 image_width;
+ u32 image_height;
+ enum decon_out_type out_type;
+ struct decon_mode mode;
+};
+
struct dpu_log {
ktime_t time;
enum dpu_event_type type;
@@ -399,6 +409,7 @@ struct dpu_log {
struct dpu_log_bts_event bts_event;
struct dpu_log_partial partial;
struct dpu_log_plane_info plane_info;
+ struct dpu_log_decon_cfg decon_cfg;
unsigned int value;
} data;
};
diff --git a/samsung/exynos_drm_drv.c b/samsung/exynos_drm_drv.c
index 081406d..a77754a 100644
--- a/samsung/exynos_drm_drv.c
+++ b/samsung/exynos_drm_drv.c
@@ -748,6 +748,17 @@ int exynos_atomic_enter_tui(void)
struct exynos_drm_connector_state *exynos_conn_state =
to_exynos_connector_state(conn_state);
+ /*
+ * TODO: revert on completion of b/241837873
+ * avoid blanked mode for panels with video operation
+ * mode
+ */
+ if (conn_state->crtc) {
+ decon = crtc_to_decon(conn_state->crtc);
+ if (decon->config.mode.op_mode == DECON_VIDEO_MODE)
+ continue;
+ }
+
exynos_conn_state->blanked_mode = true;
}
}
diff --git a/samsung/exynos_drm_fb.c b/samsung/exynos_drm_fb.c
index 97d4dda..027fbdf 100644
--- a/samsung/exynos_drm_fb.c
+++ b/samsung/exynos_drm_fb.c
@@ -15,6 +15,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
@@ -543,20 +544,59 @@ static void exynos_rmem_free(struct decon_device *decon)
decon->fb_handover.phys_size = 0;
}
-
-static void exynos_atomic_commit_tail(struct drm_atomic_state *old_state)
+static void
+exynos_disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state,
+ unsigned int *disabling_crtc_mask)
{
- int i;
- struct drm_device *dev = old_state->dev;
struct decon_device *decon;
+ struct drm_connector *connector;
+ struct drm_connector_state *old_conn_state, *new_conn_state;
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct drm_connector *connector;
- struct drm_connector_state *old_conn_state;
- struct drm_connector_state *new_conn_state;
- unsigned int disabling_crtc_mask = 0;
+ int i;
- DPU_ATRACE_BEGIN("exynos_atomic_commit_tail");
+ for_each_oldnew_connector_in_state(old_state, connector, old_conn_state,
+ new_conn_state, i) {
+ struct drm_encoder *encoder;
+ struct drm_bridge *bridge;
+
+ /* Shut down everything that's in the changeset and currently
+ * still on. So need to check the old, saved state.
+ */
+ if (!old_conn_state->crtc)
+ continue;
+
+ old_crtc_state = drm_atomic_get_old_crtc_state(old_state, old_conn_state->crtc);
+
+ if (new_conn_state->crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(
+ old_state,
+ new_conn_state->crtc);
+ else
+ new_crtc_state = NULL;
+
+ if (!exynos_crtc_needs_disable(old_crtc_state, new_crtc_state) ||
+ !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state))
+ continue;
+
+ encoder = old_conn_state->best_encoder;
+
+ /* We shouldn't get this far if we didn't previously have
+ * an encoder.. but WARN_ON() rather than explode.
+ */
+ if (WARN_ON(!encoder))
+ continue;
+
+ DRM_DEBUG_ATOMIC("disabling bridge chain [ENCODER:%d:%s]\n",
+ encoder->base.id, encoder->name);
+
+ /*
+ * Each encoder has at most one connector (since we always steal
+ * it away), so we won't call disable hooks twice.
+ */
+ bridge = drm_bridge_chain_get_first_bridge(encoder);
+ drm_atomic_bridge_chain_disable(bridge, old_state);
+ }
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state,
new_crtc_state, i) {
@@ -586,7 +626,7 @@ static void exynos_atomic_commit_tail(struct drm_atomic_state *old_state)
if (drm_atomic_crtc_effectively_active(old_crtc_state) && !new_crtc_state->active) {
/* keep runtime vote while disabling is taking place */
pm_runtime_get_sync(decon->dev);
- disabling_crtc_mask |= drm_crtc_mask(crtc);
+ *disabling_crtc_mask |= drm_crtc_mask(crtc);
}
if (old_crtc_state->active && drm_atomic_crtc_needs_modeset(new_crtc_state)) {
@@ -609,8 +649,107 @@ static void exynos_atomic_commit_tail(struct drm_atomic_state *old_state)
old_crtc_state->active = true;
}
+ for_each_oldnew_connector_in_state(old_state, connector, old_conn_state,
+ new_conn_state, i) {
+ const struct drm_encoder_helper_funcs *funcs;
+ struct drm_encoder *encoder;
+ struct drm_bridge *bridge;
+
+ /* Shut down everything that's in the changeset and currently
+ * still on. So need to check the old, saved state.
+ */
+ if (!old_conn_state->crtc)
+ continue;
+
+ old_crtc_state = drm_atomic_get_old_crtc_state(old_state, old_conn_state->crtc);
+
+ if (new_conn_state->crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(
+ old_state,
+ new_conn_state->crtc);
+ else
+ new_crtc_state = NULL;
+
+ if (!exynos_crtc_needs_disable(old_crtc_state, new_crtc_state) ||
+ !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state))
+ continue;
+
+ encoder = old_conn_state->best_encoder;
+
+ /* We shouldn't get this far if we didn't previously have
+ * an encoder.. but WARN_ON() rather than explode.
+ */
+ if (WARN_ON(!encoder))
+ continue;
+
+ funcs = encoder->helper_private;
+
+ DRM_DEBUG_ATOMIC("disabling [ENCODER:%d:%s]\n",
+ encoder->base.id, encoder->name);
+
+ /*
+ * Each encoder has at most one connector (since we always steal
+ * it away), so we won't call disable hooks twice.
+ */
+ bridge = drm_bridge_chain_get_first_bridge(encoder);
+
+ /* Right function depends upon target state. */
+ if (funcs) {
+ if (funcs->atomic_disable)
+ funcs->atomic_disable(encoder, old_state);
+ else if (new_conn_state->crtc && funcs->prepare)
+ funcs->prepare(encoder);
+ else if (funcs->disable)
+ funcs->disable(encoder);
+ else if (funcs->dpms)
+ funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+ }
+
+ drm_atomic_bridge_chain_post_disable(bridge, old_state);
+ }
+}
+
+/**
+ * exynos_drm_atomic_helper_commit_modeset_disables - modeset commit to disable outputs
+ * @dev: DRM device
+ * @old_state: atomic state object with old state structures
+ * @disabling_crtc_mask: the disabling crtc mask
+ *
+ * This function shuts down all the outputs that need to be shut down and
+ * prepares them (if required) with the new mode.
+ *
+ * For compatibility with legacy CRTC helpers this should be called before
+ * drm_atomic_helper_commit_planes(), which is what the default commit function
+ * does. But drivers with different needs can group the modeset commits together
+ * and do the plane commits at the end. This is useful for drivers doing runtime
+ * PM since planes updates then only happen when the CRTC is actually enabled.
+ */
+static void exynos_drm_atomic_helper_commit_modeset_disables(struct drm_device *dev,
+ struct drm_atomic_state *old_state, unsigned int *disabling_crtc_mask)
+{
+ exynos_disable_outputs(dev, old_state, disabling_crtc_mask);
+
+ drm_atomic_helper_update_legacy_modeset_state(dev, old_state);
+ drm_atomic_helper_calc_timestamping_constants(old_state);
+
+ exynos_crtc_set_mode(dev, old_state);
+}
+
+static void exynos_atomic_commit_tail(struct drm_atomic_state *old_state)
+{
+ int i;
+ struct drm_device *dev = old_state->dev;
+ struct decon_device *decon;
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *new_crtc_state;
+ struct drm_connector *connector;
+ struct drm_connector_state *old_conn_state;
+ struct drm_connector_state *new_conn_state;
+ unsigned int disabling_crtc_mask = 0;
+
+ DPU_ATRACE_BEGIN("exynos_atomic_commit_tail");
DPU_ATRACE_BEGIN("modeset");
- drm_atomic_helper_commit_modeset_disables(dev, old_state);
+ exynos_drm_atomic_helper_commit_modeset_disables(dev, old_state, &disabling_crtc_mask);
exynos_atomic_bts_pre_update(dev, old_state);