clear-pkgs-linux-iot-lts2018/0810-media-intel-ipu4-VIRT-...

488 lines
13 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: "Gopal, Puunithaaraj" <puunithaaraj.gopal@intel.com>
Date: Wed, 10 Oct 2018 10:38:55 +0700
Subject: [PATCH] media: intel-ipu4: [VIRT] Add support for IPU_IOC_QCMD ioctl
call
Change-Id: I341ed45dcced6f98afad82b6f9bc6535c74c35fa
Tracked-On: OAM-64123
Tracked-On: OAM-64294
Tracked-On: OAM-64937
Tracked-On: OLINUX-2973
Tracked-On: OLINUX-3042
Signed-off-by: Gopal, Puunithaaraj <puunithaaraj.gopal@intel.com>
---
drivers/media/pci/intel/ipu-psys-virt.c | 317 +++++++++++++++++-
.../intel/virtio/intel-ipu4-para-virt-psys.c | 79 ++++-
.../intel/virtio/intel-ipu4-virtio-be-psys.c | 10 +-
.../virtio/intel-ipu4-virtio-common-psys.h | 6 +
4 files changed, 409 insertions(+), 3 deletions(-)
diff --git a/drivers/media/pci/intel/ipu-psys-virt.c b/drivers/media/pci/intel/ipu-psys-virt.c
index 995f7f09c462..af5a822bfe93 100644
--- a/drivers/media/pci/intel/ipu-psys-virt.c
+++ b/drivers/media/pci/intel/ipu-psys-virt.c
@@ -149,10 +149,325 @@ int virt_ipu_psys_unmap_buf(struct ipu_psys_fh *fh,
return ipu_psys_unmapbuf(fd, fh);
}
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 2)
+static void ipu_psys_watchdog(unsigned long data)
+{
+ struct ipu_psys_kcmd *kcmd = (struct ipu_psys_kcmd *)data;
+#else
+static void ipu_psys_watchdog(struct timer_list *t)
+{
+ struct ipu_psys_kcmd *kcmd = from_timer(kcmd, t, watchdog);
+#endif
+ struct ipu_psys *psys = kcmd->fh->psys;
+
+ queue_work(IPU_PSYS_WORK_QUEUE, &psys->watchdog_work);
+}
+
+static int ipu_psys_config_legacy_pg(struct ipu_psys_kcmd *kcmd)
+{
+ struct ipu_psys *psys = kcmd->fh->psys;
+ unsigned int i;
+ int ret;
+
+ ret = ipu_fw_psys_pg_set_ipu_vaddress(kcmd, kcmd->kpg->pg_dma_addr);
+ if (ret) {
+ ret = -EIO;
+ goto error;
+ }
+
+ for (i = 0; i < kcmd->nbuffers; i++) {
+ struct ipu_fw_psys_terminal *terminal;
+ u32 buffer;
+
+ terminal = ipu_fw_psys_pg_get_terminal(kcmd, i);
+ if (!terminal)
+ continue;
+
+ buffer = (u32) kcmd->kbufs[i]->dma_addr +
+ kcmd->buffers[i].data_offset;
+
+ ret = ipu_fw_psys_terminal_set(terminal, i, kcmd,
+ buffer, kcmd->kbufs[i]->len);
+ if (ret == -EAGAIN)
+ continue;
+
+ if (ret) {
+ dev_err(&psys->adev->dev, "Unable to set terminal\n");
+ goto error;
+ }
+ }
+
+ ipu_fw_psys_pg_set_token(kcmd, (uintptr_t) kcmd);
+
+ ret = ipu_fw_psys_pg_submit(kcmd);
+ if (ret) {
+ dev_err(&psys->adev->dev, "failed to submit kcmd!\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ dev_err(&psys->adev->dev, "failed to config legacy pg\n");
+ return ret;
+}
+
+static struct ipu_psys_kcmd *virt_ipu_psys_copy_cmd(
+ struct ipu_psys_command *cmd,
+ struct ipu_psys_buffer *buffers,
+ void *pg_manifest,
+ struct ipu_psys_fh *fh)
+{
+ struct ipu_psys *psys = fh->psys;
+ struct ipu_psys_kcmd *kcmd;
+ struct ipu_psys_kbuffer *kpgbuf;
+ unsigned int i;
+ int prevfd = 0;
+
+ if (cmd->bufcount > IPU_MAX_PSYS_CMD_BUFFERS)
+ return NULL;
+
+ if (!cmd->pg_manifest_size ||
+ cmd->pg_manifest_size > KMALLOC_MAX_CACHE_SIZE)
+ return NULL;
+
+ kcmd = kzalloc(sizeof(*kcmd), GFP_KERNEL);
+ if (!kcmd)
+ return NULL;
+
+ kcmd->state = KCMD_STATE_NEW;
+ kcmd->fh = fh;
+ INIT_LIST_HEAD(&kcmd->list);
+ INIT_LIST_HEAD(&kcmd->started_list);
+
+ mutex_lock(&fh->mutex);
+ kpgbuf = ipu_psys_lookup_kbuffer(fh, cmd->pg);
+ mutex_unlock(&fh->mutex);
+ if (!kpgbuf || !kpgbuf->sgt) {
+ pr_err("%s: failed ipu_psys_lookup_kbuffer", __func__);
+ goto error;
+ }
+
+ kcmd->pg_user = kpgbuf->kaddr;
+ kcmd->kpg = __get_pg_buf(psys, kpgbuf->len);
+ if (!kcmd->kpg) {
+ pr_err("%s: failed __get_pg_buf", __func__);
+ goto error;
+ }
+
+ memcpy(kcmd->kpg->pg, kcmd->pg_user, kcmd->kpg->pg_size);
+ kcmd->pg_manifest = kzalloc(cmd->pg_manifest_size, GFP_KERNEL);
+ if (!kcmd->pg_manifest) {
+ pr_err("%s: failed kzalloc pg_manifest", __func__);
+ goto error;
+ }
+
+ memcpy(kcmd->pg_manifest, pg_manifest,
+ cmd->pg_manifest_size);
+
+ kcmd->pg_manifest_size = cmd->pg_manifest_size;
+
+ kcmd->user_token = cmd->user_token;
+ kcmd->issue_id = cmd->issue_id;
+ kcmd->priority = cmd->priority;
+ if (kcmd->priority >= IPU_PSYS_CMD_PRIORITY_NUM) {
+ pr_err("%s: failed priority", __func__);
+ goto error;
+ }
+
+ kcmd->nbuffers = ipu_fw_psys_pg_get_terminal_count(kcmd);
+ kcmd->buffers = kcalloc(kcmd->nbuffers, sizeof(*kcmd->buffers),
+ GFP_KERNEL);
+ if (!kcmd->buffers) {
+ pr_err("%s, failed kcalloc buffers", __func__);
+ goto error;
+ }
+
+ kcmd->kbufs = kcalloc(kcmd->nbuffers, sizeof(kcmd->kbufs[0]),
+ GFP_KERNEL);
+ if (!kcmd->kbufs) {
+ pr_err("%s: failed kcalloc kbufs", __func__);
+ goto error;
+ }
+
+ if (!cmd->bufcount || kcmd->nbuffers > cmd->bufcount) {
+ pr_err("%s: failed bufcount", __func__);
+ goto error;
+ }
+
+ memcpy(kcmd->buffers, buffers,
+ kcmd->nbuffers * sizeof(*kcmd->buffers));
+
+ for (i = 0; i < kcmd->nbuffers; i++) {
+ struct ipu_fw_psys_terminal *terminal;
+
+ terminal = ipu_fw_psys_pg_get_terminal(kcmd, i);
+ if (!terminal)
+ continue;
+
+
+ mutex_lock(&fh->mutex);
+ kcmd->kbufs[i] = ipu_psys_lookup_kbuffer(fh,
+ kcmd->buffers[i].base.fd);
+ mutex_unlock(&fh->mutex);
+ if (!kcmd->kbufs[i]) {
+ pr_err("%s: NULL kcmd->kbufs[i]", __func__);
+ goto error;
+ }
+ if (!kcmd->kbufs[i] || !kcmd->kbufs[i]->sgt ||
+ kcmd->kbufs[i]->len < kcmd->buffers[i].bytes_used)
+ goto error;
+ if ((kcmd->kbufs[i]->flags &
+ IPU_BUFFER_FLAG_NO_FLUSH) ||
+ (kcmd->buffers[i].flags &
+ IPU_BUFFER_FLAG_NO_FLUSH) ||
+ prevfd == kcmd->buffers[i].base.fd)
+ continue;
+
+ prevfd = kcmd->buffers[i].base.fd;
+ dma_sync_sg_for_device(&psys->adev->dev,
+ kcmd->kbufs[i]->sgt->sgl,
+ kcmd->kbufs[i]->sgt->orig_nents,
+ DMA_BIDIRECTIONAL);
+ }
+
+ return kcmd;
+
+error:
+ ipu_psys_kcmd_free(kcmd);
+
+ dev_dbg(&psys->adev->dev, "failed to copy cmd\n");
+
+ return NULL;
+}
+
+static int virt_ipu_psys_kcmd_new(struct ipu_psys_command *cmd,
+ struct ipu_psys_buffer *buffers,
+ void *pg_manifest,
+ struct ipu_psys_fh *fh)
+{
+ struct ipu_psys *psys = fh->psys;
+ struct ipu_psys_kcmd *kcmd;
+ size_t pg_size;
+ int ret = 0;
+
+ if (psys->adev->isp->flr_done)
+ return -EIO;
+
+ kcmd = virt_ipu_psys_copy_cmd(cmd, buffers, pg_manifest, fh);
+ if(!kcmd)
+ return -EINVAL;
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 2)
+ init_timer(&kcmd->watchdog);
+ kcmd->watchdog.data = (unsigned long)kcmd;
+ kcmd->watchdog.function = &ipu_psys_watchdog;
+#else
+ timer_setup(&kcmd->watchdog, ipu_psys_watchdog, 0);
+#endif
+
+ if (cmd->min_psys_freq) {
+ kcmd->constraint.min_freq = cmd->min_psys_freq;
+ ipu_buttress_add_psys_constraint(psys->adev->isp,
+ &kcmd->constraint);
+ }
+
+ pg_size = ipu_fw_psys_pg_get_size(kcmd);
+ if (pg_size > kcmd->kpg->pg_size) {
+ dev_dbg(&psys->adev->dev, "pg size mismatch %zu %zu\n",
+ pg_size, kcmd->kpg->pg_size);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ ret = ipu_psys_config_legacy_pg(kcmd);
+ if (ret)
+ goto error;
+
+ mutex_lock(&fh->mutex);
+ list_add_tail(&kcmd->list, &fh->sched.kcmds[cmd->priority]);
+ if (!fh->sched.new_kcmd_tail[cmd->priority] &&
+ kcmd->state == KCMD_STATE_NEW) {
+ fh->sched.new_kcmd_tail[cmd->priority] = kcmd;
+ /* Kick command scheduler thread */
+ atomic_set(&psys->wakeup_sched_thread_count, 1);
+ wake_up_interruptible(&psys->sched_cmd_wq);
+ }
+ mutex_unlock(&fh->mutex);
+
+ dev_dbg(&psys->adev->dev,
+ "IOC_QCMD: user_token:%llx issue_id:0x%llx pri:%d\n",
+ cmd->user_token, cmd->issue_id, cmd->priority);
+
+ return 0;
+
+error:
+ ipu_psys_kcmd_free(kcmd);
+
+ return ret;
+}
+
+
int virt_ipu_psys_qcmd(struct ipu_psys_fh *fh,
struct ipu4_virtio_req_info *req_info)
{
- return -1;
+ struct ipu_psys *psys = fh->psys;
+ struct ipu_psys_command_wrap *cmd_wrap;
+ struct ipu_psys_command *cmd;
+ void *pg_manifest;
+ struct ipu_psys_buffer *buffers;
+ int ret = 0;
+
+ if (psys->adev->isp->flr_done)
+ return -EIO;
+
+ cmd_wrap = (struct ipu_psys_command_wrap *)map_guest_phys(
+ req_info->domid,
+ req_info->request->payload,
+ PAGE_SIZE
+ );
+
+ if (cmd_wrap == NULL) {
+ pr_err("%s: failed to get payload", __func__);
+ return -EFAULT;
+ }
+
+ cmd = (struct ipu_psys_command *)map_guest_phys(
+ req_info->domid,
+ cmd_wrap->psys_command,
+ PAGE_SIZE
+ );
+
+ if (cmd == NULL) {
+ pr_err("%s: failed to get ipu_psys_command", __func__);
+ return -EFAULT;
+ }
+
+ pg_manifest = (void *)map_guest_phys(
+ req_info->domid,
+ cmd_wrap->psys_manifest,
+ PAGE_SIZE
+ );
+
+ if (pg_manifest == NULL) {
+ pr_err("%s: failed to get pg_manifest", __func__);
+ return -EFAULT;
+ }
+
+ buffers = (struct ipu_psys_buffer *)map_guest_phys(
+ req_info->domid,
+ cmd_wrap->psys_buffer,
+ PAGE_SIZE
+ );
+
+ if (buffers == NULL) {
+ pr_err("%s: failed to get ipu_psys_buffers", __func__);
+ return -EFAULT;
+ }
+
+ ret = virt_ipu_psys_kcmd_new(cmd, buffers, pg_manifest, fh);
+
+ return ret;
}
int virt_ipu_psys_dqevent(struct ipu_psys_fh *fh,
diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-psys.c b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-psys.c
index 67602a96626f..7751f471af1d 100644
--- a/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-psys.c
+++ b/drivers/media/pci/intel/virtio/intel-ipu4-para-virt-psys.c
@@ -135,6 +135,83 @@ int ipu_query_caps(struct ipu_psys_capability *caps,
return rval;
}
+int ipu_psys_kcmd_new(struct ipu_psys_command *cmd,
+ struct virt_ipu_psys_fh *fh)
+{
+ struct virt_ipu_psys *psys = fh->psys;
+ struct ipu4_virtio_req *req;
+ struct ipu4_virtio_ctx *fe_ctx = psys->ctx;
+ struct ipu_psys_command_wrap *cmd_wrap = NULL;
+ struct ipu_psys_buffer *psys_buffers = NULL;
+ void *pg_manifest = NULL;
+
+ int rval = 0;
+
+ pr_debug("%s: processing start", __func__);
+
+ req = ipu4_virtio_fe_req_queue_get();
+ if (!req)
+ return -ENOMEM;
+
+ cmd_wrap = kzalloc(sizeof(struct ipu_psys_command_wrap),
+ GFP_KERNEL);
+
+ /* Allocate for pg_manifest */
+ pg_manifest = kzalloc(cmd->pg_manifest_size, GFP_KERNEL);
+
+ /* Copy data from user */
+ if (copy_from_user(pg_manifest,
+ cmd->pg_manifest,
+ cmd->pg_manifest_size)) {
+ pr_err("%s, Failed copy_from_user", __func__);
+ rval = -EFAULT;
+ goto error_exit;
+ }
+
+
+ /* Map pg_manifest to physical address */
+ cmd_wrap->psys_manifest = virt_to_phys(pg_manifest);
+
+ /* Map ipu_psys_command to physical address */
+ cmd_wrap->psys_command = virt_to_phys(cmd);
+
+ psys_buffers = kcalloc(cmd->bufcount,
+ sizeof(struct ipu_psys_buffer),
+ GFP_KERNEL);
+
+ if (copy_from_user(psys_buffers,
+ cmd->buffers,
+ cmd->bufcount * sizeof(struct ipu_psys_buffer))) {
+ pr_err("%s, Failed copy_from_user", __func__);
+ rval = -EFAULT;
+ goto error_exit;
+ }
+
+ /* Map ipu_psys_buffer to physical address */
+ cmd_wrap->psys_buffer = virt_to_phys(psys_buffers);
+
+ req->payload = virt_to_phys(cmd_wrap);
+
+ intel_ipu4_virtio_create_req(req, IPU4_CMD_PSYS_QCMD, NULL);
+
+ rval = fe_ctx->bknd_ops->send_req(fe_ctx->domid, req, true,
+ IPU_VIRTIO_QUEUE_1);
+
+ if (rval) {
+ pr_err("%s: Failed to queue command", __func__);
+ goto error_exit;
+ }
+
+error_exit:
+ if (pg_manifest) kfree(pg_manifest);
+ if (cmd_wrap) kfree(cmd_wrap);
+ if (psys_buffers) kfree(psys_buffers);
+
+ ipu4_virtio_fe_req_queue_put(req);
+
+ return rval;
+}
+
int psys_get_userpages(struct ipu_psys_buffer *buf,
struct ipu_psys_usrptr_map *map)
{
@@ -457,7 +534,7 @@ static long virt_psys_ioctl(struct file *file, unsigned int cmd,
break;
case IPU_IOC_QCMD:
pr_debug("%s: IPU_IOC_QCMD", __func__);
- //err = ipu_psys_kcmd_new(&karg.cmd, fh);
+ err = ipu_psys_kcmd_new(&data->cmd, fh);
break;
case IPU_IOC_DQEVENT:
pr_debug("%s: IPU_IOC_DQEVENT", __func__);
diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-psys.c b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-psys.c
index de6053b293fd..e906f14b42bf 100644
--- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-psys.c
+++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-be-psys.c
@@ -63,7 +63,15 @@ int process_psys_putbuf(struct ipu4_virtio_req_info *req_info)
int process_psys_qcmd(struct ipu4_virtio_req_info *req_info)
{
- return IPU4_REQ_ERROR;
+ struct ipu_psys_fh *fh = psys_file->private_data;
+ int status = 0;
+
+ status = fh->vfops->qcmd(fh, req_info);
+
+ if (status)
+ return IPU4_REQ_ERROR;
+ else
+ return IPU4_REQ_PROCESSED;
}
int process_psys_dqevent(struct ipu4_virtio_req_info *req_info)
diff --git a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common-psys.h b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common-psys.h
index 737a2bd4ce0e..dbc421a1a32a 100644
--- a/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common-psys.h
+++ b/drivers/media/pci/intel/virtio/intel-ipu4-virtio-common-psys.h
@@ -22,4 +22,10 @@ struct ipu_psys_buffer_wrap {
struct ipu_psys_usrptr_map map;
};
+struct ipu_psys_command_wrap {
+ u64 psys_command;
+ u64 psys_manifest;
+ u64 psys_buffer;
+};
+
#endif
--
https://clearlinux.org