diff --git a/drivers/video/rockchip/mpp/mpp_common.c b/drivers/video/rockchip/mpp/mpp_common.c index 1a662c9dfe86..0c54103dba1e 100644 --- a/drivers/video/rockchip/mpp/mpp_common.c +++ b/drivers/video/rockchip/mpp/mpp_common.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -539,14 +540,15 @@ static int mpp_session_clear(struct mpp_dev *mpp, static int mpp_task_result(struct mpp_dev *mpp, struct mpp_task *task, - struct mpp_task_msgs *msgs) + struct mpp_task_msgs *msgs, + u32 no_result) { mpp_debug_enter(); if (!mpp || !task) return -EINVAL; - if (mpp->dev_ops->result) + if (mpp->dev_ops->result && !no_result) mpp->dev_ops->result(mpp, task, msgs); mpp_free_task(task->session, task); @@ -557,7 +559,8 @@ static int mpp_task_result(struct mpp_dev *mpp, } static int mpp_wait_result(struct mpp_session *session, - struct mpp_task_msgs *msgs) + struct mpp_task_msgs *msgs, + u32 no_result) { int ret; struct mpp_task *task; @@ -573,14 +576,13 @@ static int mpp_wait_result(struct mpp_session *session, !kfifo_is_empty(&session->done_fifo), msecs_to_jiffies(MPP_TIMEOUT_DELAY)); if (ret > 0) { - ret = 0; task = mpp_session_pull_done(session); - mpp_task_result(mpp, task, msgs); + ret = mpp_task_result(mpp, task, msgs, no_result); } else { mpp_err("pid %d wait %d task done timeout.\n", session->pid, atomic_read(&session->task_running)); - ret = -ETIMEDOUT; mpp_dev_abort(mpp); + ret = -ETIMEDOUT; } atomic_dec(&mpp->total_running); mpp_power_off(mpp); @@ -810,17 +812,98 @@ static int mpp_process_request(struct mpp_session *session, } break; case MPP_CMD_SET_REG_ADDR_OFFSET: { memcpy(&msgs->reg_offset, req, sizeof(*req)); - if (mpp_msg_is_last(req)) - return mpp_process_task(session, msgs); } break; case MPP_CMD_SET_REG: { memcpy(&msgs->reg_in, req, sizeof(*req)); - if (mpp_msg_is_last(req)) - return mpp_process_task(session, msgs); } break; - case MPP_CMD_GET_REG: { + case MPP_CMD_GET_REG: + case MPP_CMD_POLL_HW_FINISH: { memcpy(&msgs->reg_out, req, sizeof(*req)); - return mpp_wait_result(session, msgs); + } break; + case MPP_CMD_RESET_SESSION: { + int ret; + int val; + + ret = readx_poll_timeout(atomic_read, + &session->task_running, + val, val == 0, 1000, 50000); + if (ret == -ETIMEDOUT) { + mpp_err("wait task running time out\n"); + } else { + mpp = session->mpp; + if (!mpp) + return -EINVAL; + + mpp_session_clear(mpp, session); + down_write(&mpp->rw_sem); + ret = mpp_dma_session_destroy(session->dma); + up_write(&mpp->rw_sem); + } + return ret; + } break; + case MPP_CMD_TRANS_FD_TO_IOVA: { + u32 i; + u32 count; + u32 data[MPP_MAX_REG_TRANS_NUM]; + + mpp = session->mpp; + if (!mpp) + return -EINVAL; + + if (req->size <= 0 || + req->size > sizeof(data)) + return -EINVAL; + + memset(data, 0, sizeof(data)); + if (copy_from_user(data, req->data, req->size)) { + mpp_err("copy_from_user failed.\n"); + return -EINVAL; + } + count = req->size / sizeof(u32); + for (i = 0; i < count; i++) { + struct mpp_dma_buffer *buffer; + int fd = data[i]; + + down_read(&mpp->rw_sem); + buffer = mpp_dma_import_fd(mpp->iommu_info, + session->dma, fd); + up_read(&mpp->rw_sem); + if (IS_ERR_OR_NULL(buffer)) { + mpp_err("can not import fd %d\n", fd); + return -EINVAL; + } + data[i] = (u32)buffer->iova; + mpp_debug(DEBUG_IOMMU, "fd %d => iova %08x\n", + fd, data[i]); + } + if (copy_to_user(req->data, data, req->size)) { + mpp_err("copy_to_user failed.\n"); + return -EINVAL; + } + } break; + case MPP_CMD_RELEASE_FD: { + u32 i; + int ret; + u32 count; + u32 data[MPP_MAX_REG_TRANS_NUM]; + + if (req->size <= 0 || + req->size > sizeof(data)) + return -EINVAL; + + memset(data, 0, sizeof(data)); + if (copy_from_user(data, req->data, req->size)) { + mpp_err("copy_from_user failed.\n"); + return -EINVAL; + } + count = req->size / sizeof(u32); + for (i = 0; i < count; i++) { + ret = mpp_dma_release_fd_direct(session->dma, data[i]); + if (ret) { + mpp_err("release fd %d failed.\n", data[i]); + return ret; + } + } } break; default: { mpp = session->mpp; @@ -840,6 +923,29 @@ static int mpp_process_request(struct mpp_session *session, return 0; } +static int mpp_process_message(struct mpp_session *session, + struct mpp_task_msgs *msgs) +{ + int ret = 0; + struct mpp_request *task_msg; + + /* process message register in */ + task_msg = &msgs->reg_in; + if (task_msg->cmd == MPP_CMD_SET_REG && task_msg->size > 0) { + ret = mpp_process_task(session, msgs); + if (ret) + return ret; + } + /* process message register out */ + task_msg = &msgs->reg_out; + if (task_msg->cmd == MPP_CMD_POLL_HW_FINISH) + ret = mpp_wait_result(session, msgs, 1); + else if (task_msg->cmd == MPP_CMD_GET_REG) + ret = mpp_wait_result(session, msgs, 0); + + return ret; +} + static long mpp_dev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) @@ -898,6 +1004,12 @@ static long mpp_dev_ioctl(struct file *filp, ret = mpp_process_request(session, srv, &req, &task_msgs); if (ret) return -EFAULT; + /* last, process task message */ + if (mpp_msg_is_last(&req)) { + ret = mpp_process_message(session, &task_msgs); + if (ret) + return -EFAULT; + } } while (!mpp_msg_is_last(&req)); mpp_debug_leave(); diff --git a/drivers/video/rockchip/mpp/mpp_common.h b/drivers/video/rockchip/mpp/mpp_common.h index 299259aa5050..fd2dca8bf0eb 100644 --- a/drivers/video/rockchip/mpp/mpp_common.h +++ b/drivers/video/rockchip/mpp/mpp_common.h @@ -93,8 +93,12 @@ enum MPP_DEV_COMMAND_TYPE { MPP_CMD_POLL_BASE = 0x300, MPP_CMD_GET_REG = MPP_CMD_POLL_BASE + 0, + MPP_CMD_POLL_HW_FINISH = MPP_CMD_POLL_BASE + 1, MPP_CMD_CONTROL_BASE = 0x400, + MPP_CMD_RESET_SESSION = MPP_CMD_CONTROL_BASE + 0, + MPP_CMD_TRANS_FD_TO_IOVA = MPP_CMD_CONTROL_BASE + 1, + MPP_CMD_RELEASE_FD = MPP_CMD_CONTROL_BASE + 2, MPP_CMD_BUTT, }; diff --git a/drivers/video/rockchip/mpp/mpp_iommu.c b/drivers/video/rockchip/mpp/mpp_iommu.c index 401133d936bd..00482c5bc0a8 100644 --- a/drivers/video/rockchip/mpp/mpp_iommu.c +++ b/drivers/video/rockchip/mpp/mpp_iommu.c @@ -17,6 +17,7 @@ #include #include +#include "mpp_debug.h" #include "mpp_iommu.h" static struct mpp_dma_buffer * @@ -88,13 +89,19 @@ mpp_dma_remove_extra_buffer(struct mpp_dma_session *session) return 0; } -int mpp_dma_release(struct mpp_dma_session *session, - struct mpp_dma_buffer *buffer) +int mpp_dma_release_fd_direct(struct mpp_dma_session *session, int fd) { - if (IS_ERR_OR_NULL(buffer)) - return -EINVAL; + struct device *dev = session->dev; + struct mpp_dma_buffer *buffer = NULL; - kref_put(&buffer->ref, mpp_dma_release_buffer); + buffer = mpp_dma_find_buffer_fd(session, fd); + if (IS_ERR_OR_NULL(buffer)) { + dev_err(dev, "can not find %d buffer in list\n", fd); + + return -EINVAL; + } + + mpp_dma_release_buffer(&buffer->ref); return 0; } @@ -166,30 +173,29 @@ struct mpp_dma_buffer *mpp_dma_import_fd(struct mpp_iommu_info *iommu_info, struct mpp_dma_buffer *buffer; struct dma_buf_attachment *attach; - if (!session) + if (!session) { + mpp_err("session is null\n"); return ERR_PTR(-EINVAL); + } /* remove the oldest before add buffer */ mpp_dma_remove_extra_buffer(session); - dmabuf = dma_buf_get(fd); - if (IS_ERR(dmabuf)) - return NULL; - /* Check whether in session */ buffer = mpp_dma_find_buffer_fd(session, fd); if (!IS_ERR_OR_NULL(buffer)) { - if (buffer->dmabuf == dmabuf) { - if (kref_get_unless_zero(&buffer->ref)) { - buffer->last_used = ktime_get(); - dma_buf_put(dmabuf); - return buffer; - } + if (kref_get_unless_zero(&buffer->ref)) { + buffer->last_used = ktime_get(); + return buffer; } dev_dbg(session->dev, "missing the fd %d\n", fd); - kref_put(&buffer->ref, mpp_dma_release_buffer); } + dmabuf = dma_buf_get(fd); + if (IS_ERR(dmabuf)) { + mpp_err("dma_buf_get fd %d failed\n", fd); + return NULL; + } /* A new DMA buffer */ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); if (!buffer) { @@ -302,7 +308,7 @@ int mpp_dma_session_destroy(struct mpp_dma_session *session) list_for_each_entry_safe(buffer, n, &session->buffer_list, list) { - kref_put(&buffer->ref, mpp_dma_release_buffer); + mpp_dma_release_buffer(&buffer->ref); } kfree(session); diff --git a/drivers/video/rockchip/mpp/mpp_iommu.h b/drivers/video/rockchip/mpp/mpp_iommu.h index 07bc30185ff9..39f44e56fcf7 100644 --- a/drivers/video/rockchip/mpp/mpp_iommu.h +++ b/drivers/video/rockchip/mpp/mpp_iommu.h @@ -64,8 +64,7 @@ int mpp_dma_free(struct mpp_dma_session *session, struct mpp_dma_buffer * mpp_dma_import_fd(struct mpp_iommu_info *iommu_info, struct mpp_dma_session *session, int fd); -int mpp_dma_release(struct mpp_dma_session *session, - struct mpp_dma_buffer *buffer); +int mpp_dma_release_fd_direct(struct mpp_dma_session *session, int fd); int mpp_dma_release_fd(struct mpp_dma_session *session, int fd); int mpp_dma_unmap_kernel(struct mpp_dma_session *session,