diff options
author | PixelBot AutoMerger <android-nexus-securitybot@system.gserviceaccount.com> | 2023-02-05 19:47:23 -0800 |
---|---|---|
committer | SecurityBot <android-nexus-securitybot@system.gserviceaccount.com> | 2023-02-05 19:47:23 -0800 |
commit | 953c06fdde8ec60a2e3bfc7f35c6aa420a4d8f36 (patch) | |
tree | dfea7978e556bdcaedcc2ece674bfc644321c334 | |
parent | 66bc56f8a4113835374808b1494731107484bd01 (diff) | |
parent | 5a98b191812677d6b3f041f498ecc74a30eb58fd (diff) | |
download | display-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.c | 35 | ||||
-rw-r--r-- | samsung/exynos_drm_crtc.c | 83 | ||||
-rw-r--r-- | samsung/exynos_drm_crtc.h | 6 | ||||
-rw-r--r-- | samsung/exynos_drm_debug.c | 19 | ||||
-rw-r--r-- | samsung/exynos_drm_decon.c | 139 | ||||
-rw-r--r-- | samsung/exynos_drm_decon.h | 13 | ||||
-rw-r--r-- | samsung/exynos_drm_drv.c | 11 | ||||
-rw-r--r-- | samsung/exynos_drm_fb.c | 161 |
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); |