diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c index 9cdcabb762c0..7d42452bd920 100644 --- a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c +++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c @@ -98,6 +98,7 @@ struct scratch_mapping { struct cam_context_bank_info { struct device *dev; struct dma_iommu_mapping *mapping; + enum iommu_attr attr; dma_addr_t va_start; size_t va_len; const char *name; @@ -108,9 +109,8 @@ struct cam_context_bank_info { struct mutex lock; int handle; enum cam_smmu_ops_param state; - void (*handler[CAM_SMMU_CB_MAX])(struct iommu_domain *, - struct device *, unsigned long, - int, void*); + client_handler handler[CAM_SMMU_CB_MAX]; + client_reset_handler hw_reset_handler[CAM_SMMU_CB_MAX]; void *token[CAM_SMMU_CB_MAX]; int cb_count; int ref_cnt; @@ -354,9 +354,9 @@ static void cam_smmu_check_vaddr_in_range(int idx, void *vaddr) } void cam_smmu_reg_client_page_fault_handler(int handle, - void (*client_page_fault_handler)(struct iommu_domain *, - struct device *, unsigned long, - int, void*), void *token) + client_handler page_fault_handler, + client_reset_handler hw_reset_handler, + void *token) { int idx, i = 0; @@ -380,7 +380,7 @@ void cam_smmu_reg_client_page_fault_handler(int handle, return; } - if (client_page_fault_handler) { + if (page_fault_handler) { if (iommu_cb_set.cb_info[idx].cb_count == CAM_SMMU_CB_MAX) { pr_err("%s Should not regiester more handlers\n", iommu_cb_set.cb_info[idx].name); @@ -392,7 +392,9 @@ void cam_smmu_reg_client_page_fault_handler(int handle, if (iommu_cb_set.cb_info[idx].token[i] == NULL) { iommu_cb_set.cb_info[idx].token[i] = token; iommu_cb_set.cb_info[idx].handler[i] = - client_page_fault_handler; + page_fault_handler; + iommu_cb_set.cb_info[idx].hw_reset_handler[i] = + hw_reset_handler; break; } } @@ -420,6 +422,7 @@ static int cam_smmu_iommu_fault_handler(struct iommu_domain *domain, { char *cb_name; int idx; + int j; struct cam_smmu_work_payload *payload; if (!token) { @@ -453,6 +456,18 @@ static int cam_smmu_iommu_fault_handler(struct iommu_domain *domain, payload->token = token; payload->idx = idx; + /* trigger hw reset handler */ + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + for (j = 0; j < CAM_SMMU_CB_MAX; j++) { + if ((iommu_cb_set.cb_info[idx].hw_reset_handler[j])) { + iommu_cb_set.cb_info[idx].hw_reset_handler[j]( + payload->domain, + payload->dev, + iommu_cb_set.cb_info[idx].token[j]); + } + } + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + mutex_lock(&iommu_cb_set.payload_list_lock); list_add_tail(&payload->list, &iommu_cb_set.payload_list); mutex_unlock(&iommu_cb_set.payload_list_lock); @@ -1031,7 +1046,8 @@ static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd, buf = dma_buf_get(ion_fd); if (IS_ERR_OR_NULL(buf)) { rc = PTR_ERR(buf); - pr_err("Error: dma get buf failed. fd = %d\n", ion_fd); + pr_err("Error: dma get buf failed. fd = %d rc = %d\n", + ion_fd, rc); goto err_out; } @@ -1096,7 +1112,9 @@ static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd, rc = -ENOSPC; goto err_mapping_info; } - CDBG("ion_fd = %d, dev = %pK, paddr= %pK, len = %u\n", ion_fd, + CDBG("name %s ion_fd = %d, dev = %pK, paddr= %pK, len = %u\n", + iommu_cb_set.cb_info[idx].name, + ion_fd, (void *)iommu_cb_set.cb_info[idx].dev, (void *)*paddr_ptr, (unsigned int)*len_ptr); @@ -1305,6 +1323,47 @@ int cam_smmu_get_handle(char *identifier, int *handle_ptr) } EXPORT_SYMBOL(cam_smmu_get_handle); + +int cam_smmu_set_attr(int handle, uint32_t flags, int32_t *data) +{ + int ret = 0, idx; + struct cam_context_bank_info *cb = NULL; + struct iommu_domain *domain = NULL; + + CDBG("E: set_attr\n"); + idx = GET_SMMU_TABLE_IDX(handle); + if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) { + pr_err("Error: handle or index invalid. idx = %d hdl = %x\n", + idx, handle); + return -EINVAL; + } + mutex_lock(&iommu_cb_set.cb_info[idx].lock); + if (iommu_cb_set.cb_info[idx].handle != handle) { + pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n", + iommu_cb_set.cb_info[idx].handle, handle); + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return -EINVAL; + } + + if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_DETACH) { + domain = iommu_cb_set.cb_info[idx].mapping->domain; + cb = &iommu_cb_set.cb_info[idx]; + cb->attr |= flags; + /* set attributes */ + ret = iommu_domain_set_attr(domain, cb->attr, (void *)data); + if (ret < 0) { + pr_err("Error: set attr\n"); + return -ENODEV; + } + } else { + return -EINVAL; + } + mutex_unlock(&iommu_cb_set.cb_info[idx].lock); + return ret; +} +EXPORT_SYMBOL(cam_smmu_set_attr); + + int cam_smmu_ops(int handle, enum cam_smmu_ops_param ops) { int ret = 0, idx; diff --git a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.h b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.h index 3b8481f8bf7e..189ab2488e65 100644 --- a/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.h +++ b/drivers/media/platform/msm/camera_v2/common/cam_smmu_api.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2017, 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 @@ -49,6 +49,13 @@ enum cam_smmu_map_dir { CAM_SMMU_MAP_INVALID }; +typedef void (*client_handler)(struct iommu_domain *, + struct device *, unsigned long, + int, void*); + +typedef void (*client_reset_handler)(struct iommu_domain *, + struct device *, void*); + /** * @param identifier: Unique identifier to be used by clients which they * should get from device tree. CAM SMMU driver will @@ -61,6 +68,16 @@ enum cam_smmu_map_dir { */ int cam_smmu_get_handle(char *identifier, int *handle_ptr); + +/** + * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.) + * @param flags : SMMU attribute type + * @data : Value of attribute + * @return Status of operation. Negative in case of error. Zero otherwise. + */ +int cam_smmu_set_attr(int handle, uint32_t flags, int32_t *data); + + /** * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.) * @param op : Operation to be performed. Can be either CAM_SMMU_ATTACH @@ -215,11 +232,12 @@ int cam_smmu_find_index_by_handle(int hdl); /** * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.) * @param client_page_fault_handler: It is triggered in IOMMU page fault + * @param client_hw_reset_handler: It is triggered in IOMMU page fault * @param token: It is input param when trigger page fault handler */ void cam_smmu_reg_client_page_fault_handler(int handle, - void (*client_page_fault_handler)(struct iommu_domain *, - struct device *, unsigned long, - int, void*), void *token); + client_handler page_fault_handler, + client_reset_handler hw_reset_handler, + void *token); #endif /* _CAM_SMMU_API_H_ */ diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c index c8f5c051424e..c86ec767dd12 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c @@ -2320,7 +2320,9 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) cam_smmu_reg_client_page_fault_handler( vfe_dev->buf_mgr->iommu_hdl, - msm_vfe_iommu_fault_handler, vfe_dev); + msm_vfe_iommu_fault_handler, + NULL, + vfe_dev); mutex_unlock(&vfe_dev->core_mutex); mutex_unlock(&vfe_dev->realtime_mutex); return 0; @@ -2368,7 +2370,7 @@ int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) /* Unregister page fault handler */ cam_smmu_reg_client_page_fault_handler( vfe_dev->buf_mgr->iommu_hdl, - NULL, vfe_dev); + NULL, NULL, vfe_dev); rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1); if (rc <= 0) diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c index 8402e31364b9..bdb67f4a18de 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c @@ -424,7 +424,17 @@ static unsigned long msm_cpp_queue_buffer_info(struct cpp_device *cpp_dev, list_for_each_entry_safe(buff, save, buff_head, entry) { if (buff->map_info.buff_info.index == buffer_info->index) { - pr_err("error buffer index already queued\n"); + pr_err("error buf index already queued\n"); + pr_err("error buf, fd %d idx %d native %d ssid %d %d\n", + buffer_info->fd, buffer_info->index, + buffer_info->native_buff, + buff_queue->session_id, + buff_queue->stream_id); + pr_err("existing buf,fd %d idx %d native %d id %x\n", + buff->map_info.buff_info.fd, + buff->map_info.buff_info.index, + buff->map_info.buff_info.native_buff, + buff->map_info.buff_info.identity); goto error; } } @@ -439,6 +449,11 @@ static unsigned long msm_cpp_queue_buffer_info(struct cpp_device *cpp_dev, buff->map_info.buff_info = *buffer_info; buff->map_info.buf_fd = buffer_info->fd; + trace_printk("fd %d index %d native_buff %d ssid %d %d\n", + buffer_info->fd, buffer_info->index, + buffer_info->native_buff, buff_queue->session_id, + buff_queue->stream_id); + if (buff_queue->security_mode == SECURE_MODE) rc = cam_smmu_get_stage2_phy_addr(cpp_dev->iommu_hdl, buffer_info->fd, CAM_SMMU_MAP_RW, @@ -469,6 +484,11 @@ static void msm_cpp_dequeue_buffer_info(struct cpp_device *cpp_dev, { int ret = -1; + trace_printk("fd %d index %d native_buf %d ssid %d %d\n", + buff->map_info.buf_fd, buff->map_info.buff_info.index, + buff->map_info.buff_info.native_buff, buff_queue->session_id, + buff_queue->stream_id); + if (buff_queue->security_mode == SECURE_MODE) ret = cam_smmu_put_stage2_phy_addr(cpp_dev->iommu_hdl, buff->map_info.buf_fd); @@ -770,6 +790,36 @@ static int msm_cpp_dump_addr(struct cpp_device *cpp_dev, return 0; } +static void msm_cpp_iommu_fault_reset_handler( + struct iommu_domain *domain, struct device *dev, + void *token) +{ + struct cpp_device *cpp_dev = NULL; + + if (!token) { + pr_err("Invalid token\n"); + return; + } + + cpp_dev = token; + + if (cpp_dev->fault_status != CPP_IOMMU_FAULT_NONE) { + pr_err("fault already detected %d\n", cpp_dev->fault_status); + return; + } + + cpp_dev->fault_status = CPP_IOMMU_FAULT_DETECTED; + + /* mask IRQ status */ + msm_camera_io_w(0xB, cpp_dev->cpp_hw_base + 0xC); + + pr_err("Issue CPP HALT %d\n", cpp_dev->fault_status); + + /* MMSS_A_CPP_AXI_CMD = 0x16C, reset 0x1*/ + msm_camera_io_w(0x1, cpp_dev->cpp_hw_base + 0x16C); + +} + static void msm_cpp_iommu_fault_handler(struct iommu_domain *domain, struct device *dev, unsigned long iova, int flags, void *token) { @@ -777,50 +827,94 @@ static void msm_cpp_iommu_fault_handler(struct iommu_domain *domain, struct msm_cpp_frame_info_t *processed_frame[MAX_CPP_PROCESSING_FRAME]; int32_t i = 0, queue_len = 0; struct msm_device_queue *queue = NULL; - int32_t rc = 0; + int32_t ifd, ofd, dfd, t0fd, t1fd; + int counter = 0; + u32 result; if (token) { cpp_dev = token; + + if (cpp_dev->fault_status != CPP_IOMMU_FAULT_DETECTED) { + pr_err("fault recovery already done %d\n", + cpp_dev->fault_status); + return; + } + disable_irq(cpp_dev->irq->start); if (atomic_read(&cpp_timer.used)) { atomic_set(&cpp_timer.used, 0); del_timer_sync(&cpp_timer.cpp_timer); } - mutex_lock(&cpp_dev->mutex); tasklet_kill(&cpp_dev->cpp_tasklet); - rc = cpp_load_fw(cpp_dev, cpp_dev->fw_name_bin); - if (rc < 0) { - pr_err("load fw failure %d-retry\n", rc); - rc = msm_cpp_reset_vbif_and_load_fw(cpp_dev); - if (rc < 0) { - msm_cpp_set_micro_irq_mask(cpp_dev, 1, 0x8); - mutex_unlock(&cpp_dev->mutex); - return; - } + + pr_err("in recovery, HALT status = 0x%x\n", + msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10)); + + while (counter < MSM_CPP_POLL_RETRIES) { + result = msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10); + if (result & 0x2) + break; + usleep_range(100, 200); + counter++; } + /* MMSS_A_CPP_IRQ_STATUS_0 = 0x10 */ + pr_err("counter %d HALT status later = 0x%x\n", + counter, + msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10)); + + /* MMSS_A_CPP_RST_CMD_0 = 0x8 firmware reset = 0x3FFFF */ + msm_camera_io_w(0x3FFFF, cpp_dev->cpp_hw_base + 0x8); + + counter = 0; + while (counter < MSM_CPP_POLL_RETRIES) { + result = msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10); + if (result & 0x1) + break; + usleep_range(100, 200); + counter++; + } + + /* MMSS_A_CPP_IRQ_STATUS_0 = 0x10 */ + pr_err("counter %d after reset IRQ_STATUS_0 = 0x%x\n", + counter, + msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10)); + + /* MMSS_A_CPP_AXI_CMD = 0x16C, reset 0x1*/ + msm_camera_io_w(0x0, cpp_dev->cpp_hw_base + 0x16C); + queue = &cpp_timer.data.cpp_dev->processing_q; queue_len = queue->len; if (!queue_len) { pr_err("%s:%d: Invalid queuelen\n", __func__, __LINE__); - msm_cpp_set_micro_irq_mask(cpp_dev, 1, 0x8); - mutex_unlock(&cpp_dev->mutex); - return; } + for (i = 0; i < queue_len; i++) { if (cpp_timer.data.processed_frame[i]) { processed_frame[i] = cpp_timer.data.processed_frame[i]; - pr_err("Fault on identity=0x%x, frame_id=%03d\n", + ifd = processed_frame[i]->input_buffer_info.fd; + ofd = processed_frame[i]-> + output_buffer_info[0].fd; + dfd = processed_frame[i]-> + duplicate_buffer_info.fd; + t0fd = processed_frame[i]-> + tnr_scratch_buffer_info[0].fd; + t1fd = processed_frame[i]-> + tnr_scratch_buffer_info[1].fd; + pr_err("Fault on identity=0x%x, frame_id=%03d\n", processed_frame[i]->identity, processed_frame[i]->frame_id); + pr_err("ifd %d ofd %d dfd %d t0fd %d t1fd %d\n", + ifd, ofd, dfd, t0fd, t1fd); msm_cpp_dump_addr(cpp_dev, processed_frame[i]); msm_cpp_dump_frame_cmd(processed_frame[i]); } } msm_cpp_flush_queue_and_release_buffer(cpp_dev, queue_len); - msm_cpp_set_micro_irq_mask(cpp_dev, 1, 0x8); - mutex_unlock(&cpp_dev->mutex); + cpp_dev->fault_status = CPP_IOMMU_FAULT_RECOVERED; + pr_err("fault recovery successful\n"); } + return; } static int cpp_init_mem(struct cpp_device *cpp_dev) @@ -842,7 +936,9 @@ static int cpp_init_mem(struct cpp_device *cpp_dev) cpp_dev->iommu_hdl = iommu_hdl; cam_smmu_reg_client_page_fault_handler( cpp_dev->iommu_hdl, - msm_cpp_iommu_fault_handler, cpp_dev); + msm_cpp_iommu_fault_handler, + msm_cpp_iommu_fault_reset_handler, + cpp_dev); return 0; } @@ -981,6 +1077,7 @@ static int cpp_init_hardware(struct cpp_device *cpp_dev) int rc = 0; uint32_t vbif_version; cpp_dev->turbo_vote = 0; + cpp_dev->fault_status = CPP_IOMMU_FAULT_NONE; rc = msm_camera_regulator_enable(cpp_dev->cpp_vdd, cpp_dev->num_reg, true); @@ -1115,7 +1212,7 @@ static void cpp_release_hardware(struct cpp_device *cpp_dev) rc = msm_cpp_update_bandwidth_setting(cpp_dev, 0, 0); } cpp_dev->stream_cnt = 0; - + pr_info("cpp hw release done\n"); } static int32_t cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin) @@ -2536,6 +2633,16 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev, return -EINVAL; } + if (cpp_dev->fault_status == CPP_IOMMU_FAULT_RECOVERED) { + pr_err("Error, page fault occurred %d\n", + cpp_dev->fault_status); + return -EINVAL; + } else if (cpp_dev->fault_status == CPP_IOMMU_FAULT_DETECTED) { + pr_err("drop frame, page fault occurred %d\n", + cpp_dev->fault_status); + return -EFAULT; + } + if (cpp_frame_msg[new_frame->msg_len - 1] != MSM_CPP_MSG_ID_TRAILER) { pr_err("Invalid frame message\n"); @@ -3501,6 +3608,7 @@ STREAM_BUFF_END: break; case VIDIOC_MSM_CPP_IOMMU_ATTACH: { if (cpp_dev->iommu_state == CPP_IOMMU_STATE_DETACHED) { + int32_t stall_disable; struct msm_camera_smmu_attach_type cpp_attach_info; memset(&cpp_attach_info, 0, sizeof(cpp_attach_info)); @@ -3513,7 +3621,10 @@ STREAM_BUFF_END: } cpp_dev->security_mode = cpp_attach_info.attach; - + stall_disable = 1; + /* disable smmu stall on fault */ + cam_smmu_set_attr(cpp_dev->iommu_hdl, + DOMAIN_ATTR_CB_STALL_DISABLE, &stall_disable); if (cpp_dev->security_mode == SECURE_MODE) { rc = cam_smmu_ops(cpp_dev->iommu_hdl, CAM_SMMU_ATTACH_SEC_CPP); diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h index a05448091e42..f2c544785f46 100644 --- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h +++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h @@ -130,6 +130,12 @@ enum cpp_iommu_state { CPP_IOMMU_STATE_ATTACHED, }; +enum cpp_iommu_fault_state { + CPP_IOMMU_FAULT_NONE, + CPP_IOMMU_FAULT_DETECTED, + CPP_IOMMU_FAULT_RECOVERED, +}; + enum msm_queue { MSM_CAM_Q_CTRL, /* control command or control command status */ MSM_CAM_Q_VFE_EVT, /* adsp event */ @@ -287,6 +293,7 @@ struct cpp_device { struct msm_cpp_vbif_data *vbif_data; bool turbo_vote; struct cx_ipeak_client *cpp_cx_ipeak; + enum cpp_iommu_fault_state fault_status; }; int msm_cpp_set_micro_clk(struct cpp_device *cpp_dev);