mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
video: rockchip: mpp: add control commands to process fd
1. command to reset all fd in session, used when user space info change 2. command to translate fd into iova 3. command for release fd Change-Id: I8b9faf1d549901517563b14aaec5c178fdfc4037 Signed-off-by: Ding Wei <leo.ding@rock-chips.com>
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
@@ -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();
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/kref.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user