diff --git a/include/uapi/display/drm/sde_drm.h b/include/uapi/display/drm/sde_drm.h index 8cb12843..5eee6bd8 100644 --- a/include/uapi/display/drm/sde_drm.h +++ b/include/uapi/display/drm/sde_drm.h @@ -580,7 +580,7 @@ struct sde_drm_ubwc_stats_data { */ #define SDE_FRAME_DATA_BUFFER_MAX 0x3 #define SDE_FRAME_DATA_GUARD_BYTES 0xFF -#define SDE_FRAME_DATA_MAX_PLANES 0x10 +#define SDE_FRAME_DATA_MAX_PLANES 0x14 /** * struct sde_drm_frame_data_buffers_ctrl - control frame data buffers diff --git a/msm/dp/dp_audio.c b/msm/dp/dp_audio.c index 680f6d67..cd665aa4 100644 --- a/msm/dp/dp_audio.c +++ b/msm/dp/dp_audio.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2021, The Linux Foundation. All rights reserved. */ @@ -769,7 +770,7 @@ end: return rc; } -static int dp_audio_off(struct dp_audio *dp_audio) +static int dp_audio_off(struct dp_audio *dp_audio, bool skip_wait) { int rc = 0; struct dp_audio_private *audio; @@ -794,9 +795,11 @@ static int dp_audio_off(struct dp_audio *dp_audio) if (work_pending) DP_DEBUG("pending notification work completed\n"); - rc = dp_audio_notify(audio, EXT_DISPLAY_CABLE_DISCONNECT); - if (rc) - goto end; + if (!skip_wait) { + rc = dp_audio_notify(audio, EXT_DISPLAY_CABLE_DISCONNECT); + if (rc) + goto end; + } DP_DEBUG("success\n"); end: diff --git a/msm/dp/dp_audio.h b/msm/dp/dp_audio.h index 19e207e0..f81f0079 100644 --- a/msm/dp/dp_audio.h +++ b/msm/dp/dp_audio.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */ @@ -41,10 +42,11 @@ struct dp_audio { * playback should be stopped on the external display. * * @dp_audio: an instance of struct dp_audio. + * @skip_wait: flag to skip any waits * * Returns the error code in case of failure, 0 in success case. */ - int (*off)(struct dp_audio *dp_audio); + int (*off)(struct dp_audio *dp_audio, bool skip_wait); }; /** diff --git a/msm/dp/dp_catalog.c b/msm/dp/dp_catalog.c index e6dfae0a..8482f949 100644 --- a/msm/dp/dp_catalog.c +++ b/msm/dp/dp_catalog.c @@ -1118,6 +1118,7 @@ static void dp_catalog_panel_config_ctrl(struct dp_catalog_panel *panel, struct dp_catalog_private *catalog; struct dp_io_data *io_data; u32 strm_reg_off = 0, mainlink_ctrl; + u32 reg; if (!panel) { DP_ERR("invalid input\n"); @@ -1150,6 +1151,10 @@ static void dp_catalog_panel_config_ctrl(struct dp_catalog_panel *panel, dp_write(MMSS_DP_ASYNC_FIFO_CONFIG, 0x01); else dp_write(MMSS_DP_ASYNC_FIFO_CONFIG, 0x00); + + reg = dp_read(MMSS_DP_TIMING_ENGINE_EN); + reg |= BIT(8); + dp_write(MMSS_DP_TIMING_ENGINE_EN, reg); } static void dp_catalog_panel_config_dto(struct dp_catalog_panel *panel, @@ -1419,6 +1424,77 @@ static void dp_catalog_ctrl_usb_reset(struct dp_catalog_ctrl *ctrl, bool flip) wmb(); } +static int dp_catalog_ctrl_setup_misr(struct dp_catalog_ctrl *ctrl) +{ + struct dp_catalog_private *catalog; + struct dp_io_data *io_data; + u32 val; + + if (!ctrl) { + DP_ERR("invalid input\n"); + return -EINVAL; + } + + catalog = dp_catalog_get_priv(ctrl); + + io_data = catalog->io.dp_phy; + dp_write(DP_PHY_MISR_CTRL, 0x3); + /* make sure misr hw is reset */ + wmb(); + dp_write(DP_PHY_MISR_CTRL, 0x1); + /* make sure misr is brought out of reset */ + wmb(); + + io_data = catalog->io.dp_link; + val = 1; // frame count + val |= BIT(10); // clear status + val |= BIT(8); // enable + dp_write(DP_MISR40_CTRL, val); + /* make sure misr control is applied */ + wmb(); + + return 0; +} + +static int dp_catalog_ctrl_read_misr(struct dp_catalog_ctrl *ctrl, struct dp_misr40_data *data) +{ + struct dp_catalog_private *catalog; + struct dp_io_data *io_data; + u32 val; + int i, j; + u32 addr; + + if (!ctrl) { + DP_ERR("invalid input\n"); + return -EINVAL; + } + + catalog = dp_catalog_get_priv(ctrl); + + io_data = catalog->io.dp_phy; + val = dp_read(DP_PHY_MISR_STATUS); + if (!val) { + DP_WARN("phy misr not ready!"); + return -EAGAIN; + } + + addr = DP_PHY_MISR_TX0; + for (i = 0; i < 8; i++) { + data->phy_misr[i] = 0; + for (j = 0; j < 4; j++) { + val = dp_read(addr) & 0xff; + data->phy_misr[i] |= val << (j * 8); + addr += 4; + } + } + + io_data = catalog->io.dp_link; + for (i = 0; i < 8; i++) + data->ctrl_misr[i] = dp_read(DP_MISR40_TX0 + (i * 4)); + + return 0; +} + static void dp_catalog_panel_tpg_cfg(struct dp_catalog_panel *panel, u32 pattern) { struct dp_catalog_private *catalog; @@ -1627,6 +1703,34 @@ static bool dp_catalog_panel_dhdr_busy(struct dp_catalog_panel *panel) return dp_flush & BIT(DP_DHDR_FLUSH) ? true : false; } +static int dp_catalog_panel_get_src_crc(struct dp_catalog_panel *panel, u16 *crc) +{ + struct dp_catalog_private *catalog; + struct dp_io_data *io_data; + u32 offset; + u32 reg; + + if (panel->stream_id >= DP_STREAM_MAX) { + DP_ERR("invalid stream_id:%d\n", panel->stream_id); + return -EINVAL; + } + + catalog = dp_catalog_get_priv(panel); + io_data = catalog->io.dp_link; + + if (panel->stream_id == DP_STREAM_0) + offset = MMSS_DP_PSR_CRC_RG; + else + offset = MMSS_DP1_CRC_RG; + + reg = dp_read(offset); //GR + crc[0] = reg & 0xffff; + crc[1] = reg >> 16; + crc[2] = dp_read(offset + 4); //B + + return 0; +} + static void dp_catalog_ctrl_reset(struct dp_catalog_ctrl *ctrl) { u32 sw_reset; @@ -2896,6 +3000,8 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser) .fec_config = dp_catalog_ctrl_fec_config, .mainlink_levels = dp_catalog_ctrl_mainlink_levels, .late_phy_init = dp_catalog_ctrl_late_phy_init, + .setup_misr = dp_catalog_ctrl_setup_misr, + .read_misr = dp_catalog_ctrl_read_misr, }; struct dp_catalog_hpd hpd = { .config_hpd = dp_catalog_hpd_config_hpd, @@ -2925,6 +3031,7 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser) .pps_flush = dp_catalog_panel_pps_flush, .dhdr_flush = dp_catalog_panel_dhdr_flush, .dhdr_busy = dp_catalog_panel_dhdr_busy, + .get_src_crc = dp_catalog_panel_get_src_crc, }; if (!dev || !parser) { diff --git a/msm/dp/dp_catalog.h b/msm/dp/dp_catalog.h index fea23658..dad18559 100644 --- a/msm/dp/dp_catalog.h +++ b/msm/dp/dp_catalog.h @@ -46,6 +46,11 @@ struct dp_catalog_vsc_sdp_colorimetry { u8 data[32]; }; +struct dp_misr40_data { + u32 ctrl_misr[8]; + u32 phy_misr[8]; +}; + struct dp_catalog_aux { u32 data; u32 isr; @@ -103,6 +108,8 @@ struct dp_catalog_ctrl { int (*late_phy_init)(struct dp_catalog_ctrl *ctrl, u8 lane_cnt, bool flipped); + int (*setup_misr)(struct dp_catalog_ctrl *ctrl); + int (*read_misr)(struct dp_catalog_ctrl *ctrl, struct dp_misr40_data *data); }; struct dp_catalog_hpd { @@ -221,6 +228,7 @@ struct dp_catalog_panel { void (*pps_flush)(struct dp_catalog_panel *panel); void (*dhdr_flush)(struct dp_catalog_panel *panel); bool (*dhdr_busy)(struct dp_catalog_panel *panel); + int (*get_src_crc)(struct dp_catalog_panel *panel, u16 *crc); }; struct dp_catalog; diff --git a/msm/dp/dp_ctrl.c b/msm/dp/dp_ctrl.c index b8cad19e..2c6b6a1c 100644 --- a/msm/dp/dp_ctrl.c +++ b/msm/dp/dp_ctrl.c @@ -1308,6 +1308,7 @@ static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel) /* wait for link training completion before fec config as per spec */ dp_ctrl_fec_setup(ctrl); dp_ctrl_dsc_setup(ctrl, panel); + panel->sink_crc_enable(panel, true); return rc; } @@ -1515,6 +1516,30 @@ void dp_ctrl_set_sim_mode(struct dp_ctrl *dp_ctrl, bool en) DP_INFO("sim_mode=%d\n", ctrl->sim_mode); } +int dp_ctrl_setup_misr(struct dp_ctrl *dp_ctrl) +{ + struct dp_ctrl_private *ctrl; + + if (!dp_ctrl) + return -EINVAL; + + ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); + + return ctrl->catalog->setup_misr(ctrl->catalog); +} + +int dp_ctrl_read_misr(struct dp_ctrl *dp_ctrl, struct dp_misr40_data *data) +{ + struct dp_ctrl_private *ctrl; + + if (!dp_ctrl) + return -EINVAL; + + ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); + + return ctrl->catalog->read_misr(ctrl->catalog, data); +} + struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in) { int rc = 0; @@ -1565,6 +1590,8 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in) dp_ctrl->stream_pre_off = dp_ctrl_stream_pre_off; dp_ctrl->set_mst_channel_info = dp_ctrl_set_mst_channel_info; dp_ctrl->set_sim_mode = dp_ctrl_set_sim_mode; + dp_ctrl->setup_misr = dp_ctrl_setup_misr; + dp_ctrl->read_misr = dp_ctrl_read_misr; return dp_ctrl; error: diff --git a/msm/dp/dp_ctrl.h b/msm/dp/dp_ctrl.h index ded1d8eb..37f78d04 100644 --- a/msm/dp/dp_ctrl.h +++ b/msm/dp/dp_ctrl.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. */ @@ -32,6 +33,8 @@ struct dp_ctrl { enum dp_stream_id strm, u32 ch_start_slot, u32 ch_tot_slots); void (*set_sim_mode)(struct dp_ctrl *dp_ctrl, bool en); + int (*setup_misr)(struct dp_ctrl *dp_ctrl); + int (*read_misr)(struct dp_ctrl *dp_ctrl, struct dp_misr40_data *data); }; struct dp_ctrl_in { diff --git a/msm/dp/dp_debug.c b/msm/dp/dp_debug.c index 75b5e0ff..2897d96e 100644 --- a/msm/dp/dp_debug.c +++ b/msm/dp/dp_debug.c @@ -121,6 +121,8 @@ static void dp_debug_disable_sim_mode(struct dp_debug_private *debug, debug->sim_mode &= ~mode_mask; dp_sim_set_sim_mode(debug->sim_bridge, debug->sim_mode); + dp_sim_update_port_num(debug->sim_bridge, 0); + /* switch to normal mode */ if (!debug->sim_mode) debug->aux->set_sim_mode(debug->aux, NULL); @@ -333,7 +335,7 @@ static ssize_t dp_debug_read_dpcd(struct file *file, } } - len += scnprintf(buf + len , buf_size - len, "%04x: ", debug->dpcd_offset); + len += scnprintf(buf + len, buf_size - len, "%04x: ", debug->dpcd_offset); while (offset < debug->dpcd_size) len += scnprintf(buf + len, buf_size - len, "%02x ", dpcd[offset++]); @@ -351,6 +353,90 @@ bail: return len; } +static ssize_t dp_debug_read_crc(struct file *file, char __user *user_buff, size_t count, + loff_t *ppos) +{ + struct dp_debug_private *debug = file->private_data; + char *buf; + int const buf_size = SZ_4K; + u32 len = 0; + u16 src_crc[3] = {0}; + u16 sink_crc[3] = {0}; + struct dp_misr40_data misr40 = {0}; + u32 retries = 2; + struct drm_connector *drm_conn; + struct sde_connector *sde_conn; + struct dp_panel *panel; + int i; + int rc; + + if (!debug || !debug->aux) + return -ENODEV; + + if (*ppos) + return 0; + + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + mutex_lock(&debug->lock); + + if (!debug->panel || !debug->ctrl) + goto bail; + + if (debug->panel->mst_state) { + drm_conn = drm_connector_lookup((*debug->connector)->dev, NULL, debug->mst_con_id); + if (!drm_conn) { + DP_ERR("connector %u not in mst list\n", debug->mst_con_id); + goto bail; + } + + sde_conn = to_sde_connector(drm_conn); + panel = sde_conn->drv_panel; + } else { + panel = debug->panel; + } + + panel->get_src_crc(panel, src_crc); + panel->get_sink_crc(panel, sink_crc); + + len += scnprintf(buf + len, buf_size - len, "FRAME_CRC:\nSource vs Sink\n"); + + len += scnprintf(buf + len, buf_size - len, "CRC_R: %04X %04X\n", src_crc[0], sink_crc[0]); + len += scnprintf(buf + len, buf_size - len, "CRC_G: %04X %04X\n", src_crc[1], sink_crc[1]); + len += scnprintf(buf + len, buf_size - len, "CRC_B: %04X %04X\n", src_crc[2], sink_crc[2]); + + debug->ctrl->setup_misr(debug->ctrl); + + while (retries--) { + mutex_unlock(&debug->lock); + msleep(30); + mutex_lock(&debug->lock); + + rc = debug->ctrl->read_misr(debug->ctrl, &misr40); + if (rc != -EAGAIN) + break; + } + + len += scnprintf(buf + len, buf_size - len, "\nMISR40:\nCTLR vs PHY\n"); + for (i = 0; i < 4; i++) { + len += scnprintf(buf + len, buf_size - len, "Lane%d %08X%08X %08X%08X\n", i, + misr40.ctrl_misr[2 * i], misr40.ctrl_misr[(2 * i) + 1], + misr40.phy_misr[2 * i], misr40.phy_misr[(2 * i) + 1]); + } + + len = min_t(size_t, count, len); + if (!copy_to_user(user_buff, buf, len)) + *ppos += len; + +bail: + mutex_unlock(&debug->lock); + kfree(buf); + + return len; +} + static ssize_t dp_debug_write_hpd(struct file *file, const char __user *user_buff, size_t count, loff_t *ppos) { @@ -1871,6 +1957,11 @@ static const struct file_operations dpcd_fops = { .read = dp_debug_read_dpcd, }; +static const struct file_operations crc_fops = { + .open = simple_open, + .read = dp_debug_read_crc, +}; + static const struct file_operations connected_fops = { .open = simple_open, .read = dp_debug_read_connected, @@ -2104,6 +2195,13 @@ static int dp_debug_init_sink_caps(struct dp_debug_private *debug, return rc; } + file = debugfs_create_file("crc", 0644, dir, debug, &crc_fops); + if (IS_ERR_OR_NULL(file)) { + rc = PTR_ERR(file); + DP_ERR("[%s] debugfs crc failed, rc=%d\n", DEBUG_NAME, rc); + return rc; + } + return rc; } @@ -2370,6 +2468,8 @@ static void dp_debug_abort(struct dp_debug *dp_debug) debug = container_of(dp_debug, struct dp_debug_private, dp_debug); mutex_lock(&debug->lock); + // disconnect has already been handled. so clear hotplug + debug->hotplug = false; dp_debug_set_sim_mode(debug, false); mutex_unlock(&debug->lock); } diff --git a/msm/dp/dp_display.c b/msm/dp/dp_display.c index 141bf9ba..fe073d4b 100644 --- a/msm/dp/dp_display.c +++ b/msm/dp/dp_display.c @@ -293,7 +293,7 @@ static void dp_audio_enable(struct dp_display_private *dp, bool enable) dp->link->link_params.lane_count; dp_panel->audio->on(dp_panel->audio); } else { - dp_panel->audio->off(dp_panel->audio); + dp_panel->audio->off(dp_panel->audio, false); } } } @@ -917,7 +917,7 @@ static bool dp_display_send_hpd_event(struct dp_display_private *dp) return true; } -static int dp_display_send_hpd_notification(struct dp_display_private *dp) +static int dp_display_send_hpd_notification(struct dp_display_private *dp, bool skip_wait) { int ret = 0; bool hpd = !!dp_display_state_is(DP_STATE_CONNECTED); @@ -976,15 +976,25 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp) goto skip_wait; } - if (hpd && dp->mst.mst_active) + if (skip_wait || (hpd && dp->mst.mst_active)) goto skip_wait; if (!dp->mst.mst_active && (!!dp_display_state_is(DP_STATE_ENABLED) == hpd)) goto skip_wait; - if (!wait_for_completion_timeout(&dp->notification_comp, - HZ * 5)) { + // wait 2 seconds + if (wait_for_completion_timeout(&dp->notification_comp, HZ * 2)) + goto skip_wait; + + //resend notification + if (dp->mst.mst_active) + dp->mst.cbs.hpd(&dp->dp_display, hpd); + else + dp_display_send_hpd_event(dp); + + // wait another 3 seconds + if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 3)) { DP_WARN("%s timeout\n", hpd ? "connect" : "disconnect"); ret = -EINVAL; } @@ -1321,7 +1331,7 @@ end: } if (!rc && !dp_display_state_is(DP_STATE_ABORTED)) - dp_display_send_hpd_notification(dp); + dp_display_send_hpd_notification(dp, false); skip_notify: SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, @@ -1329,7 +1339,7 @@ skip_notify: return rc; } -static void dp_display_process_mst_hpd_low(struct dp_display_private *dp) +static void dp_display_process_mst_hpd_low(struct dp_display_private *dp, bool skip_wait) { int rc = 0; @@ -1346,7 +1356,7 @@ static void dp_display_process_mst_hpd_low(struct dp_display_private *dp) if ((dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) || dp_display_state_is(DP_STATE_ENABLED))) - rc = dp_display_send_hpd_notification(dp); + rc = dp_display_send_hpd_notification(dp, skip_wait); dp_display_set_mst_mgr_state(dp, false); dp_display_update_mst_state(dp, false); @@ -1355,7 +1365,7 @@ static void dp_display_process_mst_hpd_low(struct dp_display_private *dp) DP_MST_DEBUG("mst_hpd_low. mst_active:%d\n", dp->mst.mst_active); } -static int dp_display_process_hpd_low(struct dp_display_private *dp) +static int dp_display_process_hpd_low(struct dp_display_private *dp, bool skip_wait) { int rc = 0; @@ -1364,11 +1374,11 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp) dp_audio_enable(dp, false); if (dp->mst.mst_active) { - dp_display_process_mst_hpd_low(dp); + dp_display_process_mst_hpd_low(dp, skip_wait); } else { if ((dp_display_state_is(DP_STATE_CONNECT_NOTIFIED) || dp_display_state_is(DP_STATE_ENABLED))) - rc = dp_display_send_hpd_notification(dp); + rc = dp_display_send_hpd_notification(dp, skip_wait); } mutex_lock(&dp->session_lock); @@ -1551,7 +1561,7 @@ static void dp_display_stream_disable(struct dp_display_private *dp, dp->active_stream_cnt--; } -static void dp_display_clean(struct dp_display_private *dp) +static void dp_display_clean(struct dp_display_private *dp, bool skip_wait) { int idx; struct dp_panel *dp_panel; @@ -1579,9 +1589,10 @@ static void dp_display_clean(struct dp_display_private *dp) dp_panel = dp->active_panels[idx]; if (dp_panel->audio_supported) - dp_panel->audio->off(dp_panel->audio); + dp_panel->audio->off(dp_panel->audio, skip_wait); - dp_display_stream_pre_disable(dp, dp_panel); + if (!skip_wait) + dp_display_stream_pre_disable(dp, dp_panel); dp_display_stream_disable(dp, dp_panel); dp_display_clear_reservation(&dp->dp_display, dp_panel); dp_panel->deinit(dp_panel, 0); @@ -1593,12 +1604,12 @@ static void dp_display_clean(struct dp_display_private *dp) SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state); } -static int dp_display_handle_disconnect(struct dp_display_private *dp) +static int dp_display_handle_disconnect(struct dp_display_private *dp, bool skip_wait) { int rc; SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, dp->state); - rc = dp_display_process_hpd_low(dp); + rc = dp_display_process_hpd_low(dp, skip_wait); if (rc) { /* cancel any pending request */ dp->ctrl->abort(dp->ctrl, true); @@ -1607,7 +1618,7 @@ static int dp_display_handle_disconnect(struct dp_display_private *dp) mutex_lock(&dp->session_lock); if (dp_display_state_is(DP_STATE_ENABLED)) - dp_display_clean(dp); + dp_display_clean(dp, skip_wait); dp_display_host_unready(dp); @@ -1648,7 +1659,7 @@ static void dp_display_disconnect_sync(struct dp_display_private *dp) DP_DEBUG("disconnect delay = %d ms\n", disconnect_delay_ms); msleep(disconnect_delay_ms); - dp_display_handle_disconnect(dp); + dp_display_handle_disconnect(dp, false); SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, disconnect_delay_ms); } @@ -1762,15 +1773,16 @@ static void dp_display_attention_work(struct work_struct *work) if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) { SDE_EVT32_EXTERNAL(dp->state, DS_PORT_STATUS_CHANGED); - if (dp_display_is_sink_count_zero(dp)) { - dp_display_handle_disconnect(dp); - } else { - /* - * connect work should take care of sending - * the HPD notification. - */ - if (!dp->mst.mst_active) + if (!dp->mst.mst_active) { + if (dp_display_is_sink_count_zero(dp)) { + dp_display_handle_disconnect(dp, false); + } else { + /* + * connect work should take care of sending + * the HPD notification. + */ queue_work(dp->wq, &dp->connect_work); + } } goto mst_attention; @@ -1778,7 +1790,7 @@ static void dp_display_attention_work(struct work_struct *work) if (dp->link->sink_request & DP_TEST_LINK_VIDEO_PATTERN) { SDE_EVT32_EXTERNAL(dp->state, DP_TEST_LINK_VIDEO_PATTERN); - dp_display_handle_disconnect(dp); + dp_display_handle_disconnect(dp, false); dp->panel->video_test = true; /* @@ -1837,7 +1849,7 @@ cp_irq: * account for that. This is not needed if this * attention work was handling a test request */ - dp_display_send_hpd_notification(dp); + dp_display_send_hpd_notification(dp, false); } mst_attention: @@ -1938,7 +1950,10 @@ static int dp_display_usb_notifier(struct notifier_block *nb, SDE_EVT32_EXTERNAL(dp->state, dp->debug->sim_mode, action); if (!action && dp->debug->sim_mode) { DP_WARN("usb disconnected during simulation\n"); - dp_display_disconnect_sync(dp); + dp_display_state_add(DP_STATE_ABORTED); + dp->ctrl->abort(dp->ctrl, true); + dp->aux->abort(dp->aux, true); + dp_display_handle_disconnect(dp, true); dp->debug->abort(dp->debug); } @@ -1977,7 +1992,7 @@ int dp_display_mmrm_callback(struct mmrm_client_notifier_data *notifier_data) if (notifier_data->cb_type == MMRM_CLIENT_RESOURCE_VALUE_CHANGE && dp_display_state_is(DP_STATE_ENABLED) && !dp_display_state_is(DP_STATE_ABORTED)) { - ret = dp_display_handle_disconnect(dp); + ret = dp_display_handle_disconnect(dp, false); if (ret) DP_ERR("mmrm callback error reducing clk, ret:%d\n", ret); } @@ -2240,37 +2255,6 @@ error: return rc; } -static void dp_display_dbg_reister(struct dp_display_private *dp) -{ - struct dp_parser *parser = dp->parser; - struct dss_io_data *io; - - io = &parser->get_io(parser, "dp_ahb")->io; - if (io) - sde_dbg_reg_register_base("dp_ahb", io->base, io->len, - msm_get_phys_addr(dp->pdev, "dp_ahb"), SDE_DBG_DP); - - io = &parser->get_io(parser, "dp_aux")->io; - if (io) - sde_dbg_reg_register_base("dp_aux", io->base, io->len, - msm_get_phys_addr(dp->pdev, "dp_aux"), SDE_DBG_DP); - - io = &parser->get_io(parser, "dp_link")->io; - if (io) - sde_dbg_reg_register_base("dp_link", io->base, io->len, - msm_get_phys_addr(dp->pdev, "dp_link"), SDE_DBG_DP); - - io = &parser->get_io(parser, "dp_p0")->io; - if (io) - sde_dbg_reg_register_base("dp_p0", io->base, io->len, - msm_get_phys_addr(dp->pdev, "dp_p0"), SDE_DBG_DP); - - io = &parser->get_io(parser, "hdcp_physical")->io; - if (io) - sde_dbg_reg_register_base("hdcp_physical", io->base, io->len, - msm_get_phys_addr(dp->pdev, "hdcp_physical"), SDE_DBG_DP); -} - static int dp_display_post_init(struct dp_display *dp_display) { int rc = 0; @@ -2293,8 +2277,6 @@ static int dp_display_post_init(struct dp_display *dp_display) if (rc) goto end; - dp_display_dbg_reister(dp); - dp_display->post_init = NULL; end: DP_DEBUG("%s\n", rc ? "failed" : "success"); @@ -2682,7 +2664,7 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel) clean: if (dp_panel->audio_supported) - dp_panel->audio->off(dp_panel->audio); + dp_panel->audio->off(dp_panel->audio, false); rc = dp_display_stream_pre_disable(dp, dp_panel); diff --git a/msm/dp/dp_link.h b/msm/dp/dp_link.h index 50da7ca9..ca419f13 100644 --- a/msm/dp/dp_link.h +++ b/msm/dp/dp_link.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved. * * Copyright (c) 2008 Keith Packard @@ -38,6 +39,7 @@ struct drm_dp_aux; #define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0) +#define DP_LINK_CAP_CRC (1 << 1) struct drm_dp_link { unsigned char revision; diff --git a/msm/dp/dp_mst_sim.c b/msm/dp/dp_mst_sim.c index 2ed82571..2e3eb403 100644 --- a/msm/dp/dp_mst_sim.c +++ b/msm/dp/dp_mst_sim.c @@ -342,6 +342,10 @@ int dp_sim_update_port_num(struct dp_aux_bridge *bridge, u32 port_num) return -EINVAL; sim_dev = to_dp_sim_dev(bridge); + DP_INFO("Update port count from %d to %d\n", port_num, sim_dev->port_num); + + if (sim_dev->port_num > port_num && sim_dev->ports) + sim_dev->port_num = port_num; if (port_num > sim_dev->port_num) { ports = devm_kzalloc(sim_dev->dev, diff --git a/msm/dp/dp_mst_sim_helper.c b/msm/dp/dp_mst_sim_helper.c index 1fe4e916..1657c975 100644 --- a/msm/dp/dp_mst_sim_helper.c +++ b/msm/dp/dp_mst_sim_helper.c @@ -1086,13 +1086,15 @@ int dp_mst_sim_update(void *mst_sim_context, u32 port_num, kfree(ctx->ports); ctx->port_num = 0; - ctx->ports = kcalloc(port_num, sizeof(*ports), GFP_KERNEL); - if (!ctx->ports) { - rc = -ENOMEM; - goto fail; + if (port_num) { + ctx->ports = kcalloc(port_num, sizeof(*ports), GFP_KERNEL); + if (!ctx->ports) { + rc = -ENOMEM; + goto fail; + } + ctx->port_num = port_num; } - ctx->port_num = port_num; for (i = 0; i < port_num; i++) { ctx->ports[i] = ports[i]; if (ports[i].edid_size) { @@ -1118,6 +1120,7 @@ fail: for (i = 0; i < ctx->port_num; i++) kfree(ctx->ports[i].edid); kfree(ctx->ports); + ctx->port_num = 0; } mutex_unlock(&ctx->session_lock); diff --git a/msm/dp/dp_panel.c b/msm/dp/dp_panel.c index ace9b8ab..b9862f89 100644 --- a/msm/dp/dp_panel.c +++ b/msm/dp/dp_panel.c @@ -1682,6 +1682,10 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func) if (drm_dp_enhanced_frame_cap(dpcd)) link_info->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING; + rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_TEST_SINK_MISC, &temp, 1); + if ((rlen == 1) && (temp & DP_TEST_CRC_SUPPORTED)) + link_info->capabilities |= DP_LINK_CAP_CRC; + dfp_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] & DP_DOWN_STREAM_PORT_COUNT; @@ -3050,6 +3054,70 @@ static void dp_panel_update_pps(struct dp_panel *dp_panel, char *pps_cmd) catalog->pps_flush(catalog); } +int dp_panel_get_src_crc(struct dp_panel *dp_panel, u16 *crc) +{ + struct dp_catalog_panel *catalog; + struct dp_panel_private *panel; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + + catalog = panel->catalog; + return catalog->get_src_crc(catalog, crc); +} + +int dp_panel_get_sink_crc(struct dp_panel *dp_panel, u16 *crc) +{ + int rc = 0; + struct dp_panel_private *panel; + struct drm_dp_aux *drm_aux; + u8 crc_bytes[6]; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + drm_aux = panel->aux->drm_aux; + + /* + * At DP_TEST_CRC_R_CR, there's 6 bytes containing CRC data, 2 bytes + * per component (RGB or CrYCb). + */ + rc = drm_dp_dpcd_read(drm_aux, DP_TEST_CRC_R_CR, crc_bytes, 6); + if (rc < 0) + return rc; + + rc = 0; + crc[0] = crc_bytes[0] | crc_bytes[1] << 8; + crc[1] = crc_bytes[2] | crc_bytes[3] << 8; + crc[2] = crc_bytes[4] | crc_bytes[5] << 8; + + return rc; +} + +int dp_panel_sink_crc_enable(struct dp_panel *dp_panel, bool enable) +{ + int rc = 0; + struct dp_panel_private *panel; + struct drm_dp_aux *drm_aux; + ssize_t ret; + u8 buf; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + drm_aux = panel->aux->drm_aux; + + if (dp_panel->link_info.capabilities & DP_LINK_CAP_CRC) { + ret = drm_dp_dpcd_readb(drm_aux, DP_TEST_SINK, &buf); + if (ret < 0) + return ret; + + ret = drm_dp_dpcd_writeb(drm_aux, DP_TEST_SINK, buf | DP_TEST_SINK_START); + if (ret < 0) + return ret; + + drm_dp_dpcd_readb(drm_aux, DP_TEST_SINK, &buf); + DP_DEBUG("Enabled CRC: %x\n", buf); + } + + return rc; +} + struct dp_panel *dp_panel_get(struct dp_panel_in *in) { int rc = 0; @@ -3122,6 +3190,9 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) dp_panel->read_mst_cap = dp_panel_read_mst_cap; dp_panel->convert_to_dp_mode = dp_panel_convert_to_dp_mode; dp_panel->update_pps = dp_panel_update_pps; + dp_panel->get_src_crc = dp_panel_get_src_crc; + dp_panel->get_sink_crc = dp_panel_get_sink_crc; + dp_panel->sink_crc_enable = dp_panel_sink_crc_enable; sde_conn = to_sde_connector(dp_panel->connector); sde_conn->drv_panel = dp_panel; diff --git a/msm/dp/dp_panel.h b/msm/dp/dp_panel.h index 92b0743b..93e75901 100644 --- a/msm/dp/dp_panel.h +++ b/msm/dp/dp_panel.h @@ -193,6 +193,9 @@ struct dp_panel { const struct drm_display_mode *drm_mode, struct dp_display_mode *dp_mode); void (*update_pps)(struct dp_panel *dp_panel, char *pps_cmd); + int (*sink_crc_enable)(struct dp_panel *dp_panel, bool enable); + int (*get_src_crc)(struct dp_panel *dp_panel, u16 *crc); + int (*get_sink_crc)(struct dp_panel *dp_panel, u16 *crc); }; struct dp_tu_calc_input { diff --git a/msm/dp/dp_reg.h b/msm/dp/dp_reg.h index f48eaf6c..851291bf 100644 --- a/msm/dp/dp_reg.h +++ b/msm/dp/dp_reg.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. */ @@ -86,9 +87,16 @@ #define MMSS_DP_AUDIO_TIMING_RBR_48 (0x00000094) #define MMSS_DP_AUDIO_TIMING_HBR_48 (0x00000098) +#define DP_MISR40_CTRL (0x000000D0) +#define DP_MISR40_TX0 (0x000000D4) +#define DP_MISR40_TX1 (0x000000DC) +#define DP_MISR40_TX2 (0x000000E4) +#define DP_MISR40_TX3 (0x000000EC) #define MMSS_DP_PSR_CRC_RG (0x00000154) #define MMSS_DP_PSR_CRC_B (0x00000158) +#define MMSS_DP1_CRC_RG (0x00000164) +#define MMSS_DP1_CRC_B (0x00000168) #define DP_COMPRESSION_MODE_CTRL (0x00000180) #define DP_PPS_HB_0_3 (0x00000184) #define DP_PPS_PB_0_3 (0x00000188) @@ -384,6 +392,12 @@ #define DP_PHY_AUX_INTERRUPT_STATUS_V420 (0x00D8) #define DP_PHY_AUX_INTERRUPT_STATUS_V600 (0x00E0) #define DP_PHY_SPARE0_V420 (0x00C8) +#define DP_PHY_MISR_CTRL (0x00C0) +#define DP_PHY_MISR_STATUS (0x010C) +#define DP_PHY_MISR_TX0 (0x0110) +#define DP_PHY_MISR_TX1 (0x0130) +#define DP_PHY_MISR_TX2 (0x0150) +#define DP_PHY_MISR_TX3 (0x0170) #define TXn_TX_DRV_LVL_V420 (0x0014) #define TXn_TRANSCEIVER_BIAS_EN_V420 (0x0054) #define TXn_HIGHZ_DRVR_EN_V420 (0x0058) diff --git a/msm/dsi/dsi_catalog.h b/msm/dsi/dsi_catalog.h index c89aa728..79cd730c 100644 --- a/msm/dsi/dsi_catalog.h +++ b/msm/dsi/dsi_catalog.h @@ -314,7 +314,8 @@ void dsi_phy_hw_v5_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy, int dsi_phy_hw_v5_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings, u32 *dst, u32 size); -void dsi_phy_hw_v5_0_phy_idle_off(struct dsi_phy_hw *phy); +void dsi_phy_hw_v5_0_phy_idle_off(struct dsi_phy_hw *phy, + struct dsi_phy_cfg *cfg); void dsi_ctrl_hw_22_configure_cmddma_window(struct dsi_ctrl_hw *ctrl, struct dsi_ctrl_cmd_dma_info *cmd, u32 line_no, u32 window); diff --git a/msm/dsi/dsi_phy.c b/msm/dsi/dsi_phy.c index 00448bd2..6f7e1d2f 100644 --- a/msm/dsi/dsi_phy.c +++ b/msm/dsi/dsi_phy.c @@ -1167,11 +1167,8 @@ int dsi_phy_idle_ctrl(struct msm_dsi_phy *phy, bool enable) } else { phy->dsi_phy_state = DSI_PHY_ENGINE_OFF; - if (phy->hw.ops.disable) - phy->hw.ops.disable(&phy->hw, &phy->cfg); - if (phy->hw.ops.phy_idle_off) - phy->hw.ops.phy_idle_off(&phy->hw); + phy->hw.ops.phy_idle_off(&phy->hw, &phy->cfg); } mutex_unlock(&phy->phy_lock); diff --git a/msm/dsi/dsi_phy_hw.h b/msm/dsi/dsi_phy_hw.h index c747b294..d74e2ad7 100644 --- a/msm/dsi/dsi_phy_hw.h +++ b/msm/dsi/dsi_phy_hw.h @@ -291,8 +291,10 @@ struct dsi_phy_hw_ops { /** * phy_idle_off() - Disable PHY hardware when exiting idle screen * @phy: Pointer to DSI PHY hardware object. + * @cfg: Per lane configurations for timing, strength and lane + * configurations. */ - void (*phy_idle_off)(struct dsi_phy_hw *phy); + void (*phy_idle_off)(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg); /** * calculate_timing_params() - calculates timing parameters. diff --git a/msm/dsi/dsi_phy_hw_v5_0.c b/msm/dsi/dsi_phy_hw_v5_0.c index 7562d7b4..698e82c1 100644 --- a/msm/dsi/dsi_phy_hw_v5_0.c +++ b/msm/dsi/dsi_phy_hw_v5_0.c @@ -880,10 +880,23 @@ void dsi_phy_hw_v5_0_set_continuous_clk(struct dsi_phy_hw *phy, bool enable) wmb(); /* make sure request is set */ } -void dsi_phy_hw_v5_0_phy_idle_off(struct dsi_phy_hw *phy) +void dsi_phy_hw_v5_0_phy_idle_off(struct dsi_phy_hw *phy, + struct dsi_phy_cfg *cfg) { + if (dsi_phy_hw_v5_0_is_pll_on(phy)) + DSI_PHY_WARN(phy, "Turning OFF PHY while PLL is on\n"); + /* enable clamping of PADS */ DSI_W32(phy, DSIPHY_CMN_CTRL_4, 0x1); DSI_W32(phy, DSIPHY_CMN_CTRL_3, 0x0); wmb(); + + dsi_phy_hw_v5_0_config_lpcdrx(phy, cfg, false); + + /* Turn off REFGEN Vote */ + DSI_W32(phy, DSIPHY_CMN_GLBL_DIGTOP_SPARE10, 0x0); + /* make sure request is set */ + wmb(); + /* Delay to ensure HW removes vote*/ + udelay(2); } diff --git a/msm/dsi/dsi_pll_4nm.c b/msm/dsi/dsi_pll_4nm.c index adcf0a12..80a94f2b 100644 --- a/msm/dsi/dsi_pll_4nm.c +++ b/msm/dsi/dsi_pll_4nm.c @@ -597,11 +597,11 @@ static void dsi_pll_enable_global_clk(struct dsi_pll_resource *rsc) DSI_PLL_REG_W(rsc->phy_base, PHY_CMN_CLK_CFG1, (data | BIT(5) | BIT(4))); } -static void dsi_pll_phy_dig_reset(struct dsi_pll_resource *rsc) +static void dsi_pll_phy_analog_reset(struct dsi_pll_resource *rsc) { /* - * Reset the PHY digital domain. This would be needed when - * coming out of a CX or analog rail power collapse while + * Reset the PHY analog domain. This would be needed when + * coming out of a 0p9 power collapse while * ensuring that the pads maintain LP00 or LP11 state */ DSI_PLL_REG_W(rsc->phy_base, PHY_CMN_GLBL_DIGTOP_SPARE4, BIT(0)); @@ -1391,15 +1391,6 @@ static int dsi_pll_4nm_enable(struct dsi_pll_resource *rsc) goto error; } - /* - * assert power on reset for PHY digital in case the PLL is - * enabled after CX of analog domain power collapse. This needs - * to be done before enabling the global clk. - */ - dsi_pll_phy_dig_reset(rsc); - if (rsc->slave) - dsi_pll_phy_dig_reset(rsc->slave); - dsi_pll_enable_global_clk(rsc); if (rsc->slave) dsi_pll_enable_global_clk(rsc->slave); @@ -1436,12 +1427,53 @@ static int dsi_pll_4nm_disable(struct dsi_pll_resource *rsc) return rc; } +void dsi_pll_assert_pll_reset(struct dsi_pll_resource *rsc) +{ + u32 data = 0; + + DSI_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_1, data | BIT(7)); + + /* Ensure Assert is through */ + wmb(); + + DSI_PLL_REG_W(rsc->phy_base, PHY_CMN_CTRL_1, data & ~BIT(7)); + + /* Ensure deassert is through */ + wmb(); +} + +void dsi_pll_4nm_trigger_resets_pre_enable(struct dsi_pll_resource *rsc) +{ + /* + * Assert power on reset on DSI PHY Analog immeditately + * after 0P9 resume to make sure PHY starts in a + * clean state + */ + dsi_pll_phy_analog_reset(rsc); + if (rsc->slave) + dsi_pll_phy_analog_reset(rsc->slave); + + /* + * Trigger PLL reset as well to clear out any jitter + * introduced as result of 0p9 collapse + */ + dsi_pll_assert_pll_reset(rsc); + if (rsc->slave) + dsi_pll_assert_pll_reset(rsc->slave); +} + int dsi_pll_4nm_configure(void *pll, bool commit) { int rc = 0; struct dsi_pll_resource *rsc = (struct dsi_pll_resource *)pll; + /* These resets are needed for resetting Analog and PLL portions + * of DSI PHY before PLL is enabled and locked + */ + if (commit) + dsi_pll_4nm_trigger_resets_pre_enable(rsc); + dsi_pll_config_slave(rsc); /* PLL power needs to be enabled before accessing PLL registers */ diff --git a/msm/dsi/dsi_pll_4nm.h b/msm/dsi/dsi_pll_4nm.h index 052a2808..80056cd5 100644 --- a/msm/dsi/dsi_pll_4nm.h +++ b/msm/dsi/dsi_pll_4nm.h @@ -167,6 +167,7 @@ #define PHY_CMN_GLBL_CTRL 0x018 #define PHY_CMN_RBUF_CTRL 0x01C #define PHY_CMN_CTRL_0 0x024 +#define PHY_CMN_CTRL_1 0x028 #define PHY_CMN_CTRL_2 0x02C #define PHY_CMN_CTRL_3 0x030 #define PHY_CMN_PLL_CNTRL 0x03C diff --git a/msm/msm_drv.c b/msm/msm_drv.c index 70b803da..d43673cd 100644 --- a/msm/msm_drv.c +++ b/msm/msm_drv.c @@ -1040,6 +1040,25 @@ mdss_init_fail: return ret; } +void msm_atomic_flush_display_threads(struct msm_drm_private *priv) +{ + int i; + + if (!priv) { + SDE_ERROR("invalid private data\n"); + return; + } + + for (i = 0; i < priv->num_crtcs; i++) { + if (priv->disp_thread[i].thread) + kthread_flush_worker(&priv->disp_thread[i].worker); + if (priv->event_thread[i].thread) + kthread_flush_worker(&priv->event_thread[i].worker); + } + + kthread_flush_worker(&priv->pp_event_worker); +} + /* * DRM operations: */ @@ -1138,10 +1157,8 @@ static void msm_lastclose(struct drm_device *dev) priv->pending_crtcs); rc = kms->funcs->trigger_null_flush(kms); - if (rc) { - DRM_ERROR("null flush commit failure during lastclose\n"); + if (rc) return; - } } /* @@ -1165,6 +1182,8 @@ static void msm_lastclose(struct drm_device *dev) DRM_INFO("wait for crtc mask 0x%x failed, commit anyway...\n", priv->pending_crtcs); + msm_atomic_flush_display_threads(priv); + if (priv->fbdev) { rc = drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); if (rc) diff --git a/msm/msm_drv.h b/msm/msm_drv.h index 39f35fa0..16e91cb0 100644 --- a/msm/msm_drv.h +++ b/msm/msm_drv.h @@ -117,6 +117,7 @@ enum msm_mdp_plane_property { PLANE_PROP_DMA_GC, PLANE_PROP_FP16_GC, PLANE_PROP_FP16_CSC, + PLANE_PROP_UBWC_STATS_ROI, /* # of blob properties */ PLANE_PROP_BLOBCOUNT, @@ -140,7 +141,6 @@ enum msm_mdp_plane_property { PLANE_PROP_INVERSE_PMA, PLANE_PROP_FP16_IGC, PLANE_PROP_FP16_UNMULT, - PLANE_PROP_UBWC_STATS_ROI, /* enum/bitmask properties */ PLANE_PROP_BLEND_OP, @@ -1115,6 +1115,8 @@ struct drm_atomic_state *msm_atomic_state_alloc(struct drm_device *dev); void msm_atomic_state_clear(struct drm_atomic_state *state); void msm_atomic_state_free(struct drm_atomic_state *state); +void msm_atomic_flush_display_threads(struct msm_drm_private *priv); + int msm_gem_init_vma(struct msm_gem_address_space *aspace, struct msm_gem_vma *vma, int npages); void msm_gem_unmap_vma(struct msm_gem_address_space *aspace, diff --git a/msm/sde/sde_color_processing.c b/msm/sde/sde_color_processing.c index 1f7059ce..42181db2 100644 --- a/msm/sde/sde_color_processing.c +++ b/msm/sde/sde_color_processing.c @@ -896,13 +896,15 @@ static int _set_spr_init_feature(struct sde_hw_dspp *hw_dspp, { int ret = 0; - if (!sde_crtc || !hw_dspp || !hw_dspp->ops.setup_spr_init_config) { + if (!sde_crtc || !hw_dspp) { DRM_ERROR("invalid arguments\n"); ret = -EINVAL; } else { - hw_dspp->ops.setup_spr_init_config(hw_dspp, hw_cfg); - _update_pu_feature_enable(sde_crtc, SDE_CP_CRTC_DSPP_SPR_PU, + if (hw_dspp->ops.setup_spr_init_config) { + hw_dspp->ops.setup_spr_init_config(hw_dspp, hw_cfg); + _update_pu_feature_enable(sde_crtc, SDE_CP_CRTC_DSPP_SPR_PU, hw_cfg->payload != NULL); + } } return ret; @@ -914,12 +916,14 @@ static int _set_demura_feature(struct sde_hw_dspp *hw_dspp, { int ret = 0; - if (!hw_dspp || !hw_dspp->ops.setup_demura_cfg) { + if (!hw_dspp) { ret = -EINVAL; } else { - hw_dspp->ops.setup_demura_cfg(hw_dspp, hw_cfg); - _update_pu_feature_enable(sde_crtc, SDE_CP_CRTC_DSPP_DEMURA_PU, + if (hw_dspp->ops.setup_demura_cfg) { + hw_dspp->ops.setup_demura_cfg(hw_dspp, hw_cfg); + _update_pu_feature_enable(sde_crtc, SDE_CP_CRTC_DSPP_DEMURA_PU, hw_cfg->payload != NULL); + } } return ret; diff --git a/msm/sde/sde_connector.h b/msm/sde/sde_connector.h index 786f9464..86ff2ace 100644 --- a/msm/sde/sde_connector.h +++ b/msm/sde/sde_connector.h @@ -1217,24 +1217,45 @@ static inline int sde_connector_state_get_compression_info( return 0; } -static inline bool sde_connector_is_3d_merge_enabled(struct drm_connector *conn) +static inline bool sde_connector_is_quadpipe_3d_merge_enabled( + struct drm_connector_state *conn_state) { enum sde_rm_topology_name topology; - if (!conn) + if (!conn_state) return false; - topology = sde_connector_get_topology_name(conn); - if ((topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE) - || (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC) - || (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC) - || (topology == SDE_RM_TOPOLOGY_QUADPIPE_3DMERGE) + topology = sde_connector_get_property(conn_state, CONNECTOR_PROP_TOPOLOGY_NAME); + if ((topology == SDE_RM_TOPOLOGY_QUADPIPE_3DMERGE) || (topology == SDE_RM_TOPOLOGY_QUADPIPE_3DMERGE_DSC)) return true; return false; } +static inline bool sde_connector_is_dualpipe_3d_merge_enabled( + struct drm_connector_state *conn_state) +{ + enum sde_rm_topology_name topology; + + if (!conn_state) + return false; + + topology = sde_connector_get_property(conn_state, CONNECTOR_PROP_TOPOLOGY_NAME); + if ((topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE) + || (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_DSC) + || (topology == SDE_RM_TOPOLOGY_DUALPIPE_3DMERGE_VDC)) + return true; + + return false; +} + +static inline bool sde_connector_is_3d_merge_enabled(struct drm_connector_state *conn_state) +{ + return sde_connector_is_dualpipe_3d_merge_enabled(conn_state) + || sde_connector_is_quadpipe_3d_merge_enabled(conn_state); +} + /** * sde_connector_set_msm_mode - set msm_mode for connector state * @conn_state: Pointer to drm connector state structure diff --git a/msm/sde/sde_crtc.c b/msm/sde/sde_crtc.c index ae47ea51..fa50d111 100644 --- a/msm/sde/sde_crtc.c +++ b/msm/sde/sde_crtc.c @@ -1364,6 +1364,8 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc, u32 crtc_width, crtc_height, mixer_width, mixer_height; struct drm_display_mode *adj_mode; int rc = 0, lm_idx, i; + struct drm_connector *conn; + struct drm_connector_state *conn_state; if (!crtc || !state) return -EINVAL; @@ -1379,14 +1381,27 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc, sde_crtc_get_resolution(crtc, state, adj_mode, &crtc_width, &crtc_height); sde_crtc_get_mixer_resolution(crtc, state, adj_mode, &mixer_width, &mixer_height); /* check cumulative mixer w/h is equal full crtc w/h */ - if (sde_crtc->num_mixers - && (((mixer_width * sde_crtc->num_mixers) != crtc_width) + if (sde_crtc->num_mixers && (((mixer_width * sde_crtc->num_mixers) != crtc_width) || (mixer_height != crtc_height))) { SDE_ERROR("%s: invalid w/h crtc:%d,%d, mixer:%d,%d, num_mixers:%d\n", sde_crtc->name, crtc_width, crtc_height, mixer_width, mixer_height, sde_crtc->num_mixers); rc = -EINVAL; goto end; + } else if (state->state) { + for_each_new_connector_in_state(state->state, conn, conn_state, i) { + if (conn_state && (conn_state->crtc == crtc) + && ((sde_connector_is_dualpipe_3d_merge_enabled(conn_state) + && (crtc_width % 4)) + || (sde_connector_is_quadpipe_3d_merge_enabled(conn_state) + && (crtc_width % 8)))) { + SDE_ERROR( + "%s: invalid 3d-merge_w - mixer_w:%d, crtc_w:%d, num_mixers:%d\n", + sde_crtc->name, mixer_width, + crtc_width, sde_crtc->num_mixers); + return -EINVAL; + } + } } /* @@ -1406,7 +1421,7 @@ static int _sde_crtc_check_rois(struct drm_crtc *crtc, goto end; } - if (sde_connector_is_3d_merge_enabled(conn) && (mixer_width % 2)) { + if (sde_connector_is_3d_merge_enabled(conn->state) && (mixer_width % 2)) { SDE_ERROR( "%s: invalid width w/ 3d-merge - mixer_w:%d, crtc_w:%d, num_mixers:%d\n", sde_crtc->name, crtc_width, mixer_width, sde_crtc->num_mixers); @@ -2763,8 +2778,10 @@ void sde_crtc_get_frame_data(struct drm_crtc *crtc) data->frame_count = sde_crtc->fps_info.frame_count; /* Collect plane specific data */ - drm_for_each_plane_mask(plane, crtc->dev, sde_crtc->plane_mask_old) - sde_plane_get_frame_data(plane, &data->plane_frame_data[i]); + drm_for_each_plane_mask(plane, crtc->dev, sde_crtc->plane_mask_old) { + if (i < SDE_FRAME_DATA_MAX_PLANES) + sde_plane_get_frame_data(plane, &data->plane_frame_data[i++]); + } if (frame_data->cnt) _sde_crtc_frame_data_notify(crtc, data); diff --git a/msm/sde/sde_encoder_phys_wb.c b/msm/sde/sde_encoder_phys_wb.c index 44e713a5..f1fa5243 100644 --- a/msm/sde/sde_encoder_phys_wb.c +++ b/msm/sde/sde_encoder_phys_wb.c @@ -1024,8 +1024,8 @@ static int sde_encoder_phys_wb_atomic_check(struct sde_encoder_phys *phys_enc, /* bypass check if commit with no framebuffer */ fb = sde_wb_connector_state_get_output_fb(conn_state); if (!fb) { - SDE_DEBUG("[enc:%d wb:%d] no out fb\n", DRMID(phys_enc->parent), WBID(wb_enc)); - return 0; + SDE_ERROR("[enc:%d wb:%d] no out fb\n", DRMID(phys_enc->parent), WBID(wb_enc)); + return -EINVAL; } fmt = sde_get_sde_format_ext(fb->format->format, fb->modifier); @@ -1430,23 +1430,32 @@ static void _sde_encoder_phys_wb_setup_dnsc_blur(struct sde_encoder_phys *phys_e int i; bool enable; - if (!sde_kms->catalog->dnsc_blur_count || !hw_dnsc_blur || !hw_pp - || !hw_dnsc_blur->ops.setup_dnsc_blur) + if (!sde_kms->catalog->dnsc_blur_count || !hw_pp) return; sde_conn = to_sde_connector(wb_dev->connector); sde_conn_state = to_sde_connector_state(wb_dev->connector->state); - if (sde_conn_state->dnsc_blur_count && !hw_dnsc_blur) { + if (sde_conn_state->dnsc_blur_count + && (!hw_dnsc_blur || !hw_dnsc_blur->ops.setup_dnsc_blur)) { SDE_ERROR("[enc:%d wb:%d] invalid config - dnsc_blur block not reserved\n", DRMID(phys_enc->parent), WBID(wb_enc)); - sde_kms->catalog->dnsc_blur_count = 0; return; } /* swap between 0 & 1 lut idx on each config change for gaussian lut */ sde_conn_state->dnsc_blur_lut = 1 - sde_conn_state->dnsc_blur_lut; + /* + * disable dnsc_blur case - safe to update the opmode as dynamic switching of + * dnsc_blur hw block between WBs are not supported currently. + */ + if (hw_dnsc_blur && !sde_conn_state->dnsc_blur_count) { + hw_dnsc_blur->ops.setup_dnsc_blur(hw_dnsc_blur, NULL, 0); + SDE_EVT32(DRMID(phys_enc->parent), WBID(wb_enc), SDE_EVTLOG_FUNC_CASE1); + return; + } + for (i = 0; i < sde_conn_state->dnsc_blur_count; i++) { cfg = &sde_conn_state->dnsc_blur_cfg[i]; diff --git a/msm/sde/sde_kms.c b/msm/sde/sde_kms.c index e176b1aa..bea70fb8 100644 --- a/msm/sde/sde_kms.c +++ b/msm/sde/sde_kms.c @@ -2655,6 +2655,11 @@ error: drm_framebuffer_put(fb); } + drm_for_each_crtc(crtc, dev) { + if (!ret && crtc_mask & drm_crtc_mask(crtc)) + sde_kms_cancel_delayed_work(crtc); + } + end: return ret; } @@ -3881,6 +3886,7 @@ static int sde_kms_trigger_null_flush(struct msm_kms *kms) { struct sde_kms *sde_kms; struct sde_splash_display *splash_display; + struct drm_crtc *crtc; int i, rc = 0; if (!kms) { @@ -3890,28 +3896,42 @@ static int sde_kms_trigger_null_flush(struct msm_kms *kms) sde_kms = to_sde_kms(kms); - if (!sde_kms->splash_data.num_splash_displays || - sde_kms->dsi_display_count == sde_kms->splash_data.num_splash_displays) - return rc; + /* If splash handoff is done, early return*/ + if (!sde_kms->splash_data.num_splash_displays) + return 0; + /* If all builtin-displays are having cont splash enabled, ignore lastclose*/ + if (sde_kms->dsi_display_count == sde_kms->splash_data.num_splash_displays) + return -EINVAL; + + /* + * Trigger NULL flush if built-in secondary/primary is stuck in splash + * while the primary/secondary is running respectively before lastclose. + */ for (i = 0; i < MAX_DSI_DISPLAYS; i++) { splash_display = &sde_kms->splash_data.splash_display[i]; if (splash_display->cont_splash_enabled && splash_display->encoder) { + crtc = splash_display->encoder->crtc; SDE_DEBUG("triggering null commit on enc:%d\n", DRMID(splash_display->encoder)); SDE_EVT32(DRMID(splash_display->encoder), SDE_EVTLOG_FUNC_ENTRY); rc = _sde_kms_null_commit(sde_kms->dev, splash_display->encoder); + + if (!rc && crtc) + sde_kms_cancel_delayed_work(crtc); + if (rc) + DRM_ERROR("null flush commit failure during lastclose\n"); } } - return rc; + return 0; } static void _sde_kms_pm_suspend_idle_helper(struct sde_kms *sde_kms, struct device *dev) { - int i, ret, crtc_id = 0; + int ret, crtc_id = 0; struct drm_device *ddev = dev_get_drvdata(dev); struct drm_connector *conn; struct drm_connector_list_iter conn_iter; @@ -3948,15 +3968,7 @@ static void _sde_kms_pm_suspend_idle_helper(struct sde_kms *sde_kms, } drm_connector_list_iter_end(&conn_iter); - for (i = 0; i < priv->num_crtcs; i++) { - if (priv->disp_thread[i].thread) - kthread_flush_worker( - &priv->disp_thread[i].worker); - if (priv->event_thread[i].thread) - kthread_flush_worker( - &priv->event_thread[i].worker); - } - kthread_flush_worker(&priv->pp_event_worker); + msm_atomic_flush_display_threads(priv); } struct msm_display_mode *sde_kms_get_msm_mode(struct drm_connector_state *conn_state) diff --git a/msm/sde/sde_plane.c b/msm/sde/sde_plane.c index cb7ddb50..09f90d6f 100644 --- a/msm/sde/sde_plane.c +++ b/msm/sde/sde_plane.c @@ -3992,8 +3992,8 @@ static void _sde_plane_install_properties(struct drm_plane *plane, PLANE_PROP_FB_TRANSLATION_MODE); if (psde->pipe_hw->ops.set_ubwc_stats_roi) - msm_property_install_range(&psde->property_info, "ubwc_stats_roi", - 0, 0, 0xFFFFFFFF, 0, PLANE_PROP_UBWC_STATS_ROI); + msm_property_install_volatile_range(&psde->property_info, "ubwc_stats_roi", + 0, 0, ~0, 0, PLANE_PROP_UBWC_STATS_ROI); vfree(info); } diff --git a/msm/sde_dbg.c b/msm/sde_dbg.c index 2eacf1f8..7aef13e9 100644 --- a/msm/sde_dbg.c +++ b/msm/sde_dbg.c @@ -1191,7 +1191,6 @@ void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog) char buf[SDE_EVTLOG_BUF_MAX]; bool update_last_entry = true; u32 in_log, in_mem, in_dump; - u32 log_size = 0; char *dump_addr = NULL; int i; @@ -1202,21 +1201,22 @@ void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog) in_mem = evtlog->dump_mode & SDE_DBG_DUMP_IN_MEM; in_dump = evtlog->dump_mode & SDE_DBG_DUMP_IN_COREDUMP; - log_size = sde_evtlog_count(evtlog); - if (!log_size) - return; - if (!evtlog->dumped_evtlog) { - if (in_mem) - log_size = SDE_EVTLOG_ENTRY; - evtlog->dumped_evtlog = kvzalloc((log_size * SDE_EVTLOG_BUF_MAX), GFP_KERNEL); - evtlog->log_size = log_size; + evtlog->dumped_evtlog = kvzalloc((SDE_EVTLOG_ENTRY * SDE_EVTLOG_BUF_MAX), + GFP_KERNEL); + if (!evtlog->dumped_evtlog) + return; + + evtlog->log_size = SDE_EVTLOG_ENTRY; } dump_addr = evtlog->dumped_evtlog; if ((in_mem || in_dump) && dump_addr && (!sde_dbg_base.coredump_reading)) { - while (sde_evtlog_dump_to_buffer(evtlog, dump_addr, SDE_EVTLOG_BUF_MAX, - update_last_entry, true)) { + for (i = 0; i < evtlog->log_size; i++) { + if (!sde_evtlog_dump_to_buffer(evtlog, dump_addr, SDE_EVTLOG_BUF_MAX, + update_last_entry, true)) + break; + dump_addr += SDE_EVTLOG_BUF_MAX; update_last_entry = false; } @@ -1232,8 +1232,11 @@ void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog) } if (in_log) { - while (sde_evtlog_dump_to_buffer(evtlog, buf, sizeof(buf), - update_last_entry, false)) { + for (i = 0; i < evtlog->log_size; i++) { + if (!sde_evtlog_dump_to_buffer(evtlog, buf, SDE_EVTLOG_BUF_MAX, + update_last_entry, false)) + break; + pr_info("%s\n", buf); update_last_entry = false; }