diff options
| -rw-r--r-- | Documentation/kernel-parameters.txt | 2 | ||||
| -rw-r--r-- | drivers/char/diag/diag_dci.c | 126 | ||||
| -rw-r--r-- | drivers/gpu/drm/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/gpu/drm/Makefile | 2 | ||||
| -rw-r--r-- | drivers/gpu/drm/drm_edid.c | 15 | ||||
| -rw-r--r-- | drivers/gpu/drm/drm_probe_helper.c | 17 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c | 8 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/msm_atomic.c | 24 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/msm_drv.h | 3 | ||||
| -rw-r--r-- | drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c | 28 | ||||
| -rw-r--r-- | drivers/soc/qcom/icnss.c | 109 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_tx.c | 5 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_util.c | 8 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_mdp_overlay.c | 14 | ||||
| -rw-r--r-- | include/soc/qcom/icnss.h | 4 |
15 files changed, 272 insertions, 95 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 69b096e6ac45..f9500fb1498b 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -948,7 +948,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. The filter can be disabled or changed to another driver later using sysfs. - drm_kms_helper.edid_firmware=[<connector>:]<file>[,[<connector>:]<file>] + drm.edid_firmware=[<connector>:]<file>[,[<connector>:]<file>] Broken monitors, graphic adapters, KVMs and EDIDless panels may send no or incorrect EDID data sets. This parameter allows to specify an EDID data sets diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c index d18768b0e9da..19fbd702c0e6 100644 --- a/drivers/char/diag/diag_dci.c +++ b/drivers/char/diag/diag_dci.c @@ -727,32 +727,47 @@ int diag_dci_query_event_mask(struct diag_dci_client_tbl *entry, return ((*event_mask_ptr & byte_mask) == byte_mask) ? 1 : 0; } -static int diag_dci_filter_commands(struct diag_pkt_header_t *header) +static int diag_dci_filter_commands(struct diag_pkt_header_t *header, + int header_len) { if (!header) return -ENOMEM; - switch (header->cmd_code) { - case 0x7d: /* Msg Mask Configuration */ - case 0x73: /* Log Mask Configuration */ - case 0x81: /* Event Mask Configuration */ - case 0x82: /* Event Mask Change */ - case 0x60: /* Event Mask Toggle */ - return 1; - } + if (header_len <= 0) + return -EIO; - if (header->cmd_code == 0x4b && header->subsys_id == 0x12) { - switch (header->subsys_cmd_code) { - case 0x60: /* Extended Event Mask Config */ - case 0x61: /* Extended Msg Mask Config */ - case 0x62: /* Extended Log Mask Config */ - case 0x20C: /* Set current Preset ID */ - case 0x20D: /* Get current Preset ID */ - case 0x218: /* HDLC Disabled Command */ + if (header_len) { + switch (header->cmd_code) { + case 0x7d: /* Msg Mask Configuration */ + case 0x73: /* Log Mask Configuration */ + case 0x81: /* Event Mask Configuration */ + case 0x82: /* Event Mask Change */ + case 0x60: /* Event Mask Toggle */ + DIAG_LOG(DIAG_DEBUG_DCI, + "diag: command not supported: %d\n", + header->cmd_code); return 1; } } + if (header_len >= (3*sizeof(uint8_t))) { + if (header->cmd_code == 0x4b && header->subsys_id == 0x12) { + switch (header->subsys_cmd_code) { + case 0x60: /* Extended Event Mask Config */ + case 0x61: /* Extended Msg Mask Config */ + case 0x62: /* Extended Log Mask Config */ + case 0x20C: /* Set current Preset ID */ + case 0x20D: /* Get current Preset ID */ + case 0x218: /* HDLC Disabled Command */ + DIAG_LOG(DIAG_DEBUG_DCI, + "diag: command not supported %d %d %d\n", + header->cmd_code, header->subsys_id, + header->subsys_cmd_code); + return 1; + } + } + } + return 0; } @@ -1796,7 +1811,7 @@ int diag_dci_send_handshake_pkt(int index) static int diag_dci_process_apps_pkt(struct diag_pkt_header_t *pkt_header, unsigned char *req_buf, int req_len, - int tag) + int tag, int pkt_header_len) { uint8_t cmd_code, subsys_id, i, goto_download = 0; uint8_t header_len = sizeof(struct diag_dci_pkt_header_t); @@ -1806,12 +1821,16 @@ static int diag_dci_process_apps_pkt(struct diag_pkt_header_t *pkt_header, unsigned char *payload_ptr = driver->apps_dci_buf + header_len; struct diag_dci_pkt_header_t dci_header; - if (!pkt_header || !req_buf || req_len <= 0 || tag < 0) + if (!pkt_header || !req_buf || req_len <= 0 || tag < 0 || + pkt_header_len <= 0) return -EIO; - cmd_code = pkt_header->cmd_code; - subsys_id = pkt_header->subsys_id; - ss_cmd_code = pkt_header->subsys_cmd_code; + if (pkt_header_len >= (sizeof(uint8_t))) + cmd_code = pkt_header->cmd_code; + if (pkt_header_len >= (2 * sizeof(uint8_t))) + subsys_id = pkt_header->subsys_id; + if (pkt_header_len >= (3 * sizeof(uint8_t))) + ss_cmd_code = pkt_header->subsys_cmd_code; if (cmd_code == DIAG_CMD_DOWNLOAD) { *payload_ptr = DIAG_CMD_DOWNLOAD; @@ -1931,7 +1950,7 @@ fill_buffer: static int diag_process_dci_pkt_rsp(unsigned char *buf, int len) { int ret = DIAG_DCI_TABLE_ERR; - int common_cmd = 0; + int common_cmd = 0, header_len = 0; struct diag_pkt_header_t *header = NULL; unsigned char *temp = buf; unsigned char *req_buf = NULL; @@ -1947,8 +1966,7 @@ static int diag_process_dci_pkt_rsp(unsigned char *buf, int len) if (!buf) return -EIO; - if (len < (sizeof(struct dci_pkt_req_t) + - sizeof(struct diag_pkt_header_t)) || + if (len < sizeof(struct dci_pkt_req_t) || len > DCI_REQ_BUF_SIZE) { pr_err("diag: dci: Invalid length %d len in %s", len, __func__); return -EIO; @@ -1959,13 +1977,6 @@ static int diag_process_dci_pkt_rsp(unsigned char *buf, int len) read_len += sizeof(struct dci_pkt_req_t); req_len -= sizeof(struct dci_pkt_req_t); req_buf = temp; /* Start of the Request */ - header = (struct diag_pkt_header_t *)temp; - read_len += sizeof(struct diag_pkt_header_t); - if (read_len >= DCI_REQ_BUF_SIZE) { - pr_err("diag: dci: In %s, invalid read_len: %d\n", __func__, - read_len); - return -EIO; - } mutex_lock(&driver->dci_mutex); dci_entry = diag_dci_get_client_entry(req_hdr.client_id); @@ -1976,11 +1987,40 @@ static int diag_process_dci_pkt_rsp(unsigned char *buf, int len) return DIAG_DCI_NO_REG; } + header = (void *)temp; + header_len = len - sizeof(struct dci_pkt_req_t); + if (header_len <= 0) { + mutex_unlock(&driver->dci_mutex); + return -EIO; + } + if (header_len >= sizeof(uint8_t)) { + header->cmd_code = (uint16_t)(*(uint8_t *)temp); + read_len += sizeof(uint8_t); + } + if (header_len >= (2 * sizeof(uint8_t))) { + temp += sizeof(uint8_t); + header->subsys_id = (uint16_t)(*(uint8_t *)temp); + read_len += sizeof(uint8_t); + } + if (header_len == (3 * sizeof(uint8_t))) { + temp += sizeof(uint8_t); + header->subsys_cmd_code = (uint16_t)(*(uint8_t *)temp); + read_len += sizeof(uint8_t); + } else if (header_len >= + (2 * sizeof(uint8_t)) + sizeof(uint16_t)) { + temp += sizeof(uint8_t); + header->subsys_cmd_code = (uint16_t)(*(uint16_t *)temp); + read_len += sizeof(uint16_t); + } + if (read_len > DCI_REQ_BUF_SIZE) { + pr_err("diag: dci: In %s, invalid read_len: %d\n", __func__, + read_len); + mutex_unlock(&driver->dci_mutex); + return -EIO; + } + /* Check if the command is allowed on DCI */ - if (diag_dci_filter_commands(header)) { - pr_debug("diag: command not supported %d %d %d", - header->cmd_code, header->subsys_id, - header->subsys_cmd_code); + if (diag_dci_filter_commands(header, header_len)) { mutex_unlock(&driver->dci_mutex); return DIAG_DCI_SEND_DATA_FAIL; } @@ -2034,14 +2074,18 @@ static int diag_process_dci_pkt_rsp(unsigned char *buf, int len) /* Check if it is a dedicated Apps command */ ret = diag_dci_process_apps_pkt(header, req_buf, req_len, - req_entry->tag); + req_entry->tag, header_len); if ((ret == DIAG_DCI_NO_ERROR && !common_cmd) || ret < 0) return ret; - reg_entry.cmd_code = header->cmd_code; - reg_entry.subsys_id = header->subsys_id; - reg_entry.cmd_code_hi = header->subsys_cmd_code; - reg_entry.cmd_code_lo = header->subsys_cmd_code; + if (header_len >= (sizeof(uint8_t))) + reg_entry.cmd_code = header->cmd_code; + if (header_len >= (2 * sizeof(uint8_t))) + reg_entry.subsys_id = header->subsys_id; + if (header_len >= (3 * sizeof(uint8_t))) { + reg_entry.cmd_code_hi = header->subsys_cmd_code; + reg_entry.cmd_code_lo = header->subsys_cmd_code; + } mutex_lock(&driver->cmd_reg_mutex); temp_entry = diag_cmd_search(®_entry, ALL_PROC); diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 021d8391abea..65d6e1db8db8 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -63,7 +63,7 @@ config DRM_FBDEV_EMULATION config DRM_LOAD_EDID_FIRMWARE bool "Allow to specify an EDID data set instead of probing for it" - depends on DRM_KMS_HELPER + depends on DRM help Say Y here, if you want to use EDID data to be loaded from the /lib/firmware directory or one of the provided built-in diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index ea0d5ea57213..d4e3cf4356d7 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -20,12 +20,12 @@ drm-$(CONFIG_PCI) += ati_pcigart.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-$(CONFIG_OF) += drm_of.o drm-$(CONFIG_AGP) += drm_agpsupport.o +drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm-y += $(drm-m) drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o -drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index fbd717324328..d3f3e33230f2 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1501,6 +1501,10 @@ drm_do_probe_ddc_edid(void *data, u8 *buf, unsigned int block, size_t len) * level, drivers must make all reasonable efforts to expose it as an I2C * adapter and use drm_get_edid() instead of abusing this function. * + * The EDID may be overridden using debugfs override_edid or firmare EDID + * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority + * order. Having either of them bypasses actual EDID reads. + * * Return: Pointer to valid EDID or NULL if we couldn't find any. */ struct edid *drm_do_get_edid(struct drm_connector *connector, @@ -1511,6 +1515,17 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, int i, j = 0, valid_extensions = 0; u8 *block, *new; bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); + struct edid *override = NULL; + + if (connector->override_edid) + override = drm_edid_duplicate((const struct edid *) + connector->edid_blob_ptr->data); + + if (!override) + override = drm_load_edid_firmware(connector); + + if (!IS_ERR_OR_NULL(override)) + return override; if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL) return NULL; diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 7c6fc77c0b7b..81dfc23e1c6a 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -199,22 +199,7 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect goto prune; } - if (connector->override_edid) { - struct edid *edid = (struct edid *) connector->edid_blob_ptr->data; - - count = drm_add_edid_modes(connector, edid); - drm_edid_to_eld(connector, edid); - } else { - struct edid *edid = drm_load_edid_firmware(connector); - if (!IS_ERR_OR_NULL(edid)) { - drm_mode_connector_update_edid_property(connector, edid); - count = drm_add_edid_modes(connector, edid); - drm_edid_to_eld(connector, edid); - kfree(edid); - } - if (count == 0) - count = (*connector_funcs->get_modes)(connector); - } + count = (*connector_funcs->get_modes)(connector); if (count == 0 && connector->status == connector_status_connected) count = drm_add_modes_noedid(connector, 1024, 768); diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c index 252a6289881f..75543c768d45 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -123,6 +123,9 @@ static ssize_t debugfs_state_info_read(struct file *file, dsi_ctrl->clk_info.link_clks.pixel_clk_rate, dsi_ctrl->clk_info.link_clks.esc_clk_rate); + if (len > count) + len = count; + /* TODO: make sure that this does not exceed 4K */ if (copy_to_user(buff, buf, len)) { kfree(buf); @@ -162,6 +165,9 @@ static ssize_t debugfs_reg_dump_read(struct file *file, "Core clocks are not turned on, cannot read\n"); } + if (len > count) + len = count; + /* TODO: make sure that this does not exceed 4K */ if (copy_to_user(buff, buf, len)) { kfree(buf); diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c index 99f89841a74a..8467be8c3f0b 100644 --- a/drivers/gpu/drm/msm/msm_atomic.c +++ b/drivers/gpu/drm/msm/msm_atomic.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * Copyright (C) 2014 Red Hat * Author: Rob Clark <robdclark@gmail.com> * @@ -27,22 +27,26 @@ struct msm_commit { uint32_t fence; struct msm_fence_cb fence_cb; uint32_t crtc_mask; + uint32_t plane_mask; struct kthread_work commit_work; }; /* block until specified crtcs are no longer pending update, and * atomically mark them as pending update */ -static int start_atomic(struct msm_drm_private *priv, uint32_t crtc_mask) +static int start_atomic(struct msm_drm_private *priv, uint32_t crtc_mask, + uint32_t plane_mask) { int ret; spin_lock(&priv->pending_crtcs_event.lock); ret = wait_event_interruptible_locked(priv->pending_crtcs_event, - !(priv->pending_crtcs & crtc_mask)); + !(priv->pending_crtcs & crtc_mask) && + !(priv->pending_planes & plane_mask)); if (ret == 0) { DBG("start: %08x", crtc_mask); priv->pending_crtcs |= crtc_mask; + priv->pending_planes |= plane_mask; } spin_unlock(&priv->pending_crtcs_event.lock); @@ -51,18 +55,21 @@ static int start_atomic(struct msm_drm_private *priv, uint32_t crtc_mask) /* clear specified crtcs (no longer pending update) */ -static void end_atomic(struct msm_drm_private *priv, uint32_t crtc_mask) +static void end_atomic(struct msm_drm_private *priv, uint32_t crtc_mask, + uint32_t plane_mask) { spin_lock(&priv->pending_crtcs_event.lock); DBG("end: %08x", crtc_mask); priv->pending_crtcs &= ~crtc_mask; + priv->pending_planes &= ~plane_mask; wake_up_all_locked(&priv->pending_crtcs_event); spin_unlock(&priv->pending_crtcs_event.lock); } static void commit_destroy(struct msm_commit *commit) { - end_atomic(commit->dev->dev_private, commit->crtc_mask); + end_atomic(commit->dev->dev_private, commit->crtc_mask, + commit->plane_mask); kfree(commit); } @@ -593,7 +600,7 @@ int msm_atomic_commit(struct drm_device *dev, struct drm_crtc *crtc = state->crtcs[i]; if (!crtc) continue; - commit->crtc_mask |= (1 << drm_crtc_index(crtc)); + commit->crtc_mask |= (1 << i); } /* @@ -608,13 +615,16 @@ int msm_atomic_commit(struct drm_device *dev, if ((plane->state->fb != new_state->fb) && new_state->fb) commit_set_fence(commit, new_state->fb); + + commit->plane_mask |= (1 << i); } /* * Wait for pending updates on any of the same crtc's and then * mark our set of crtc's as busy: */ - ret = start_atomic(dev->dev_private, commit->crtc_mask); + ret = start_atomic(dev->dev_private, commit->crtc_mask, + commit->plane_mask); if (ret) { DRM_ERROR("start_atomic failed: %d\n", ret); commit_destroy(commit); diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index 87649f7ec3dd..a9c28feb11ce 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> * @@ -319,6 +319,7 @@ struct msm_drm_private { /* crtcs pending async atomic updates: */ uint32_t pending_crtcs; + uint32_t pending_planes; wait_queue_head_t pending_crtcs_event; /* Registered address spaces.. currently this is fixed per # of diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index f021707a53cd..1af55fe257af 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -616,12 +616,9 @@ static void msm_isp_update_framedrop_reg(struct msm_vfe_axi_stream *stream_info, MSM_VFE_STREAM_STOP_PERIOD; } - if (stream_info->undelivered_request_cnt > 0 && - drop_reconfig != 1) + if (stream_info->undelivered_request_cnt > 0) stream_info->current_framedrop_period = MSM_VFE_STREAM_STOP_PERIOD; - if (stream_info->controllable_output && drop_reconfig == 1) - stream_info->current_framedrop_period = 1; /* * re-configure the period pattern, only if it's not already * set to what we want @@ -3624,9 +3621,10 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, (stream_info->undelivered_request_cnt <= MAX_BUFFERS_IN_HW) ) { - pr_debug("%s:%d invalid time to request frame %d\n", + pr_debug("%s:%d invalid time to request frame %d try drop_reconfig\n", __func__, __LINE__, frame_id); vfe_dev->isp_page->drop_reconfig = 1; + return 0; } else if ((vfe_dev->axi_data.src_info[frame_src].active) && ((frame_id == vfe_dev->axi_data.src_info[frame_src].frame_id) || @@ -3634,10 +3632,11 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, (stream_info->undelivered_request_cnt <= MAX_BUFFERS_IN_HW)) { vfe_dev->isp_page->drop_reconfig = 1; - pr_debug("%s: vfe_%d request_frame %d cur frame id %d pix %d\n", + pr_debug("%s: vfe_%d request_frame %d cur frame id %d pix %d try drop_reconfig\n", __func__, vfe_dev->pdev->id, frame_id, vfe_dev->axi_data.src_info[VFE_PIX_0].frame_id, vfe_dev->axi_data.src_info[VFE_PIX_0].active); + return 0; } else if ((vfe_dev->axi_data.src_info[frame_src].active && (frame_id != vfe_dev->axi_data.src_info[frame_src].frame_id + vfe_dev-> axi_data.src_info[frame_src].sof_counter_step)) || @@ -3658,19 +3657,18 @@ static int msm_isp_request_frame(struct vfe_device *vfe_dev, if ((frame_src == VFE_PIX_0) && !stream_info->undelivered_request_cnt && MSM_VFE_STREAM_STOP_PERIOD != stream_info->activated_framedrop_period) { + /* wm is reloaded if undelivered_request_cnt is zero. + * As per the hw behavior wm should be disabled or skip writing + * before reload happens other wise wm could start writing from + * middle of the frame and could result in image corruption. + * instead of dropping frame in this error scenario use + * drop_reconfig flag to process the request in next sof. + */ pr_debug("%s:%d vfe %d frame_id %d prev_pattern %x stream_id %x\n", __func__, __LINE__, vfe_dev->pdev->id, frame_id, stream_info->activated_framedrop_period, stream_info->stream_id); - - rc = msm_isp_return_empty_buffer(vfe_dev, stream_info, - user_stream_id, frame_id, buf_index, frame_src); - if (rc < 0) - pr_err("%s:%d failed: return_empty_buffer src %d\n", - __func__, __LINE__, frame_src); - stream_info->current_framedrop_period = - MSM_VFE_STREAM_STOP_PERIOD; - msm_isp_cfg_framedrop_reg(stream_info); + vfe_dev->isp_page->drop_reconfig = 1; return 0; } diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index 17bfb06a5651..714a4f16cd01 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -196,6 +196,8 @@ enum icnss_driver_event_type { ICNSS_DRIVER_EVENT_REGISTER_DRIVER, ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER, ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN, + ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN, + ICNSS_DRIVER_EVENT_IDLE_RESTART, ICNSS_DRIVER_EVENT_MAX, }; @@ -303,6 +305,7 @@ enum icnss_driver_state { ICNSS_REJUVENATE, ICNSS_BLOCK_SHUTDOWN, ICNSS_PDR, + ICNSS_MODEM_CRASHED, }; struct ce_irq_list { @@ -636,6 +639,10 @@ static char *icnss_driver_event_to_str(enum icnss_driver_event_type type) return "UNREGISTER_DRIVER"; case ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN: return "PD_SERVICE_DOWN"; + case ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN: + return "IDLE_SHUTDOWN"; + case ICNSS_DRIVER_EVENT_IDLE_RESTART: + return "IDLE_RESTART"; case ICNSS_DRIVER_EVENT_MAX: return "EVENT_MAX"; } @@ -2303,6 +2310,7 @@ static int icnss_pd_restart_complete(struct icnss_priv *priv) icnss_call_driver_shutdown(priv); clear_bit(ICNSS_PDR, &priv->state); + clear_bit(ICNSS_MODEM_CRASHED, &priv->state); clear_bit(ICNSS_REJUVENATE, &priv->state); clear_bit(ICNSS_PD_RESTART, &priv->state); priv->is_ssr = false; @@ -2494,6 +2502,52 @@ out: return ret; } +static int icnss_driver_event_idle_shutdown(void *data) +{ + int ret = 0; + + if (!penv->ops || !penv->ops->idle_shutdown) + return 0; + + if (test_bit(ICNSS_MODEM_CRASHED, &penv->state) || + test_bit(ICNSS_PDR, &penv->state) || + test_bit(ICNSS_REJUVENATE, &penv->state)) { + icnss_pr_err("SSR/PDR is already in-progress during idle shutdown callback\n"); + ret = -EBUSY; + } else { + icnss_pr_dbg("Calling driver idle shutdown, state: 0x%lx\n", + penv->state); + icnss_block_shutdown(true); + ret = penv->ops->idle_shutdown(&penv->pdev->dev); + icnss_block_shutdown(false); + } + + return ret; +} + +static int icnss_driver_event_idle_restart(void *data) +{ + int ret = 0; + + if (!penv->ops || !penv->ops->idle_restart) + return 0; + + if (test_bit(ICNSS_MODEM_CRASHED, &penv->state) || + test_bit(ICNSS_PDR, &penv->state) || + test_bit(ICNSS_REJUVENATE, &penv->state)) { + icnss_pr_err("SSR/PDR is already in-progress during idle restart callback\n"); + ret = -EBUSY; + } else { + icnss_pr_dbg("Calling driver idle restart, state: 0x%lx\n", + penv->state); + icnss_block_shutdown(true); + ret = penv->ops->idle_restart(&penv->pdev->dev); + icnss_block_shutdown(false); + } + + return ret; +} + static void icnss_driver_event_work(struct work_struct *work) { struct icnss_driver_event *event; @@ -2535,6 +2589,12 @@ static void icnss_driver_event_work(struct work_struct *work) ret = icnss_driver_event_pd_service_down(penv, event->data); break; + case ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN: + ret = icnss_driver_event_idle_shutdown(event->data); + break; + case ICNSS_DRIVER_EVENT_IDLE_RESTART: + ret = icnss_driver_event_idle_restart(event->data); + break; default: icnss_pr_err("Invalid Event type: %d", event->type); kfree(event); @@ -2641,6 +2701,9 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb, priv->is_ssr = true; + if (notif->crashed) + set_bit(ICNSS_MODEM_CRASHED, &priv->state); + if (code == SUBSYS_BEFORE_SHUTDOWN && !notif->crashed && test_bit(ICNSS_BLOCK_SHUTDOWN, &priv->state)) { if (!wait_for_completion_timeout(&priv->unblock_shutdown, @@ -3597,6 +3660,48 @@ out: } EXPORT_SYMBOL(icnss_trigger_recovery); +int icnss_idle_shutdown(struct device *dev) +{ + struct icnss_priv *priv = dev_get_drvdata(dev); + + if (!priv) { + icnss_pr_err("Invalid drvdata: dev %pK", dev); + return -EINVAL; + } + + if (test_bit(ICNSS_MODEM_CRASHED, &priv->state) || + test_bit(ICNSS_PDR, &priv->state) || + test_bit(ICNSS_REJUVENATE, &penv->state)) { + icnss_pr_err("SSR/PDR is already in-progress during idle shutdown\n"); + return -EBUSY; + } + + return icnss_driver_event_post(ICNSS_DRIVER_EVENT_IDLE_SHUTDOWN, + ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL); +} +EXPORT_SYMBOL(icnss_idle_shutdown); + +int icnss_idle_restart(struct device *dev) +{ + struct icnss_priv *priv = dev_get_drvdata(dev); + + if (!priv) { + icnss_pr_err("Invalid drvdata: dev %pK", dev); + return -EINVAL; + } + + if (test_bit(ICNSS_MODEM_CRASHED, &priv->state) || + test_bit(ICNSS_PDR, &priv->state) || + test_bit(ICNSS_REJUVENATE, &penv->state)) { + icnss_pr_err("SSR/PDR is already in-progress during idle restart\n"); + return -EBUSY; + } + + return icnss_driver_event_post(ICNSS_DRIVER_EVENT_IDLE_RESTART, + ICNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL); +} +EXPORT_SYMBOL(icnss_idle_restart); + static int icnss_smmu_init(struct icnss_priv *priv) { @@ -4053,6 +4158,10 @@ static int icnss_stats_show_state(struct seq_file *s, struct icnss_priv *priv) continue; case ICNSS_PDR: seq_puts(s, "PDR TRIGGERED"); + continue; + case ICNSS_MODEM_CRASHED: + seq_puts(s, "MODEM CRASHED"); + continue; } seq_printf(s, "UNKNOWN-%d", i); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 4f30f7864bb0..0778e43fe0ff 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, 2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -3742,7 +3742,8 @@ static int hdmi_tx_hdcp_off(struct hdmi_tx_ctrl *hdmi_ctrl) DEV_DBG("%s: Turning off HDCP\n", __func__); hdmi_ctrl->hdcp_ops->off(hdmi_ctrl->hdcp_data); - flush_delayed_work(&hdmi_ctrl->hdcp_cb_work); + hdmi_ctrl->hdcp_status = HDCP_STATE_INACTIVE; + cancel_delayed_work(&hdmi_ctrl->hdcp_cb_work); hdmi_ctrl->hdcp_ops = NULL; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_util.c b/drivers/video/fbdev/msm/mdss_hdmi_util.c index c7a9c530eedb..8e854c54bae8 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_util.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_util.c @@ -790,11 +790,13 @@ static void hdmi_ddc_trigger(struct hdmi_tx_ddc_ctrl *ddc_ctrl, if (mode == TRIGGER_READ && seg) { DSS_REG_W_ND(io, HDMI_DDC_DATA, BIT(31) | (seg_addr << 8)); DSS_REG_W_ND(io, HDMI_DDC_DATA, seg_num << 8); + DSS_REG_W_ND(io, HDMI_DDC_DATA, (ddc_data->dev_addr << 8)); + } else { + /* handle portion #1 */ + DSS_REG_W_ND(io, HDMI_DDC_DATA, + BIT(31) | (ddc_data->dev_addr << 8)); } - /* handle portion #1 */ - DSS_REG_W_ND(io, HDMI_DDC_DATA, BIT(31) | (ddc_data->dev_addr << 8)); - /* handle portion #2 */ DSS_REG_W_ND(io, HDMI_DDC_DATA, ddc_data->offset << 8); diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 324e5e9ca60d..daeed5d45072 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -5762,12 +5762,14 @@ static int mdss_mdp_overlay_on(struct msm_fb_data_type *mfd) panel_on: if (mdp5_data->vsync_en) { - pr_info("reenabling vsync for fb%d\n", mfd->index); - mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); - rc = ctl->ops.add_vsync_handler(ctl, &ctl->vsync_handler); - mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); + if ((ctl) && (ctl->ops.add_vsync_handler)) { + pr_info("reenabling vsync for fb%d\n", mfd->index); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); + rc = ctl->ops.add_vsync_handler(ctl, + &ctl->vsync_handler); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); + } } - if (IS_ERR_VALUE(rc)) { pr_err("Failed to turn on fb%d\n", mfd->index); mdss_mdp_overlay_off(mfd); diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h index 4de4cd5e89dc..716e28054e60 100644 --- a/include/soc/qcom/icnss.h +++ b/include/soc/qcom/icnss.h @@ -56,6 +56,8 @@ struct icnss_driver_ops { int (*suspend_noirq)(struct device *dev); int (*resume_noirq)(struct device *dev); int (*uevent)(struct device *dev, struct icnss_uevent_data *uevent); + int (*idle_shutdown)(struct device *dev); + int (*idle_restart)(struct device *dev); }; @@ -159,4 +161,6 @@ extern u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num); extern int icnss_trigger_recovery(struct device *dev); extern void icnss_block_shutdown(bool status); extern bool icnss_is_pdr(void); +extern int icnss_idle_restart(struct device *dev); +extern int icnss_idle_shutdown(struct device *dev); #endif /* _ICNSS_WLAN_H_ */ |
