Implement the request API on top of display lists. Queueing a request creates and queues a display list containing the pipeline configuration for processing, allowing back-to-back operation without waiting for frame processing completion before preparing the pipeline for the next frame. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@xxxxxxxxxxxxxxxx> --- drivers/media/platform/vsp1/Makefile | 3 +- drivers/media/platform/vsp1/vsp1_dl.c | 3 + drivers/media/platform/vsp1/vsp1_dl.h | 1 + drivers/media/platform/vsp1/vsp1_drv.c | 9 +++ drivers/media/platform/vsp1/vsp1_pipe.c | 71 ++++++++++++++++ drivers/media/platform/vsp1/vsp1_pipe.h | 7 ++ drivers/media/platform/vsp1/vsp1_request.c | 126 +++++++++++++++++++++++++++++ drivers/media/platform/vsp1/vsp1_request.h | 41 ++++++++++ drivers/media/platform/vsp1/vsp1_video.c | 84 ++++++++----------- 9 files changed, 295 insertions(+), 50 deletions(-) create mode 100644 drivers/media/platform/vsp1/vsp1_request.c create mode 100644 drivers/media/platform/vsp1/vsp1_request.h diff --git a/drivers/media/platform/vsp1/Makefile b/drivers/media/platform/vsp1/Makefile index 95b3ac2ea7ef..5df1346290d6 100644 --- a/drivers/media/platform/vsp1/Makefile +++ b/drivers/media/platform/vsp1/Makefile @@ -1,5 +1,6 @@ vsp1-y := vsp1_drv.o vsp1_entity.o vsp1_pipe.o -vsp1-y += vsp1_dl.o vsp1_drm.o vsp1_video.o +vsp1-y += vsp1_dl.o vsp1_request.o +vsp1-y += vsp1_drm.o vsp1_video.o vsp1-y += vsp1_rpf.o vsp1_rwpf.o vsp1_wpf.o vsp1-y += vsp1_hsit.o vsp1_lif.o vsp1_lut.o vsp1-y += vsp1_bru.o vsp1_sru.o vsp1_uds.o diff --git a/drivers/media/platform/vsp1/vsp1_dl.c b/drivers/media/platform/vsp1/vsp1_dl.c index 43597b38a433..1181757d5081 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.c +++ b/drivers/media/platform/vsp1/vsp1_dl.c @@ -15,9 +15,12 @@ #include <linux/dma-mapping.h> #include <linux/gfp.h> +#include <media/v4l2-device.h> + #include "vsp1.h" #include "vsp1_dl.h" #include "vsp1_pipe.h" +#include "vsp1_video.h" /* * Global resources diff --git a/drivers/media/platform/vsp1/vsp1_dl.h b/drivers/media/platform/vsp1/vsp1_dl.h index 571ed6d8e7c2..bc77db7ad4d1 100644 --- a/drivers/media/platform/vsp1/vsp1_dl.h +++ b/drivers/media/platform/vsp1/vsp1_dl.h @@ -15,6 +15,7 @@ #include <linux/types.h> +struct v4l2_device; struct vsp1_device; struct vsp1_dl_list; struct vsp1_dl_manager; diff --git a/drivers/media/platform/vsp1/vsp1_drv.c b/drivers/media/platform/vsp1/vsp1_drv.c index e0bcc67e2d07..d8bc92b7d5de 100644 --- a/drivers/media/platform/vsp1/vsp1_drv.c +++ b/drivers/media/platform/vsp1/vsp1_drv.c @@ -30,6 +30,7 @@ #include "vsp1_hsit.h" #include "vsp1_lif.h" #include "vsp1_lut.h" +#include "vsp1_request.h" #include "vsp1_rwpf.h" #include "vsp1_sru.h" #include "vsp1_uds.h" @@ -210,6 +211,12 @@ static void vsp1_destroy_entities(struct vsp1_device *vsp1) vsp1_drm_cleanup(vsp1); } +static const struct media_device_ops vsp1_media_device_ops = { + .req_alloc = vsp1_request_alloc, + .req_free = vsp1_request_free, + .req_queue = vsp1_request_queue, +}; + static int vsp1_create_entities(struct vsp1_device *vsp1) { struct media_device *mdev = &vsp1->media_dev; @@ -219,6 +226,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) int ret; mdev->dev = vsp1->dev; + mdev->ops = &vsp1_media_device_ops; strlcpy(mdev->model, "VSP1", sizeof(mdev->model)); snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", dev_name(mdev->dev)); @@ -238,6 +246,7 @@ static int vsp1_create_entities(struct vsp1_device *vsp1) vsp1->media_ops.link_validate = v4l2_subdev_link_validate; vdev->mdev = mdev; + ret = v4l2_device_register(vsp1->dev, vdev); if (ret < 0) { dev_err(vsp1->dev, "V4L2 device registration failed (%d)\n", diff --git a/drivers/media/platform/vsp1/vsp1_pipe.c b/drivers/media/platform/vsp1/vsp1_pipe.c index 38157a2e4cbc..b055b963e567 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.c +++ b/drivers/media/platform/vsp1/vsp1_pipe.c @@ -23,8 +23,10 @@ #include "vsp1_dl.h" #include "vsp1_entity.h" #include "vsp1_pipe.h" +#include "vsp1_request.h" #include "vsp1_rwpf.h" #include "vsp1_uds.h" +#include "vsp1_video.h" /* ----------------------------------------------------------------------------- * Helper Functions @@ -136,6 +138,40 @@ const struct vsp1_format_info *vsp1_get_format_info(u32 fourcc) return NULL; } + +/* ----------------------------------------------------------------------------- + * Requests Management + */ + +void vsp1_pipeline_queue_request(struct vsp1_pipeline *pipe, + struct vsp1_request *req) +{ + struct vsp1_video *video; + unsigned long flags; + unsigned int i; + + media_device_request_get(&req->req); + + spin_lock_irqsave(&pipe->irqlock, flags); + list_add_tail(&req->list, &pipe->requests); + spin_unlock_irqrestore(&pipe->irqlock, flags); + + for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { + if (!pipe->inputs[i]) + continue; + + video = pipe->inputs[i]->video; + mutex_lock(&video->lock); + vb2_qbuf_request(&video->queue, req->req.id, NULL); + mutex_unlock(&video->lock); + } + + video = pipe->output->video; + mutex_lock(&video->lock); + vb2_qbuf_request(&video->queue, req->req.id, NULL); + mutex_unlock(&video->lock); +} + /* ----------------------------------------------------------------------------- * Pipeline Management */ @@ -155,6 +191,7 @@ void vsp1_pipeline_reset(struct vsp1_pipeline *pipe) pipe->inputs[i] = NULL; INIT_LIST_HEAD(&pipe->entities); + INIT_LIST_HEAD(&pipe->requests); pipe->state = VSP1_PIPELINE_STOPPED; pipe->buffers_ready = 0; pipe->num_inputs = 0; @@ -171,6 +208,7 @@ void vsp1_pipeline_init(struct vsp1_pipeline *pipe) init_waitqueue_head(&pipe->wq); INIT_LIST_HEAD(&pipe->entities); + INIT_LIST_HEAD(&pipe->requests); pipe->state = VSP1_PIPELINE_STOPPED; } @@ -250,6 +288,39 @@ bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe) return pipe->buffers_ready == mask; } +void vsp1_pipeline_setup(struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, + struct media_device_request *req) +{ + struct vsp1_entity *entity; + + if (pipe->uds) { + struct vsp1_uds *uds = to_uds(&pipe->uds->subdev); + + /* If a BRU is present in the pipeline before the UDS, the alpha + * component doesn't need to be scaled as the BRU output alpha + * value is fixed to 255. Otherwise we need to scale the alpha + * component only when available at the input RPF. + */ + if (pipe->uds_input->type == VSP1_ENTITY_BRU) { + uds->scale_alpha = false; + } else { + const struct vsp1_format_info *info; + struct vsp1_rwpf *rpf = + to_rwpf(&pipe->uds_input->subdev); + + info = vsp1_get_format_info(rpf->format.pixelformat); + uds->scale_alpha = info->alpha; + } + } + + list_for_each_entry(entity, &pipe->entities, list_pipe) { + vsp1_entity_route_setup(entity, dl); + + if (entity->ops->configure) + entity->ops->configure(entity, dl, req); + } +} + void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe) { if (pipe == NULL) diff --git a/drivers/media/platform/vsp1/vsp1_pipe.h b/drivers/media/platform/vsp1/vsp1_pipe.h index 1100229a1ed2..a26e4a7ae67d 100644 --- a/drivers/media/platform/vsp1/vsp1_pipe.h +++ b/drivers/media/platform/vsp1/vsp1_pipe.h @@ -20,6 +20,7 @@ #include <media/media-entity.h> struct vsp1_dl_list; +struct vsp1_request; struct vsp1_rwpf; /* @@ -74,6 +75,7 @@ enum vsp1_pipeline_state { * @uds: UDS entity, if present * @uds_input: entity at the input of the UDS, if the UDS is present * @entities: list of entities in the pipeline + * @requests: list of pending requests * @dl: display list associated with the pipeline */ struct vsp1_pipeline { @@ -100,6 +102,7 @@ struct vsp1_pipeline { struct list_head entities; + struct list_head requests; struct vsp1_dl_list *dl; }; @@ -118,6 +121,10 @@ void vsp1_pipeline_run(struct vsp1_pipeline *pipe); bool vsp1_pipeline_stopped(struct vsp1_pipeline *pipe); int vsp1_pipeline_stop(struct vsp1_pipeline *pipe); bool vsp1_pipeline_ready(struct vsp1_pipeline *pipe); +void vsp1_pipeline_setup(struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, + struct media_device_request *req); +void vsp1_pipeline_queue_request(struct vsp1_pipeline *pipe, + struct vsp1_request *req); void vsp1_pipeline_frame_end(struct vsp1_pipeline *pipe); diff --git a/drivers/media/platform/vsp1/vsp1_request.c b/drivers/media/platform/vsp1/vsp1_request.c new file mode 100644 index 000000000000..d78289f18fc0 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_request.c @@ -0,0 +1,126 @@ +/* + * vsp1_request.c -- R-Car VSP1 Request Management + * + * Copyright (C) 2015 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@xxxxxxxxxxxxxxxx) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/delay.h> +#include <linux/list.h> +#include <linux/wait.h> + +#include <media/media-entity.h> +#include <media/v4l2-subdev.h> + +#include "vsp1.h" +#include "vsp1_dl.h" +#include "vsp1_entity.h" +#include "vsp1_pipe.h" +#include "vsp1_request.h" +#include "vsp1_rwpf.h" +#include "vsp1_video.h" + +struct media_device_request *vsp1_request_alloc(struct media_device *mdev) +{ + struct vsp1_request *req; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return NULL; + + return &req->req; +} + +void vsp1_request_free(struct media_device *mdev, + struct media_device_request *mreq) +{ + struct vsp1_request *req = to_vsp1_request(mreq); + + kfree(req); +} + +int vsp1_request_queue(struct media_device *mdev, + struct media_device_request *mreq) +{ + struct vsp1_device *vsp1 = + container_of(mdev, struct vsp1_device, media_dev); + struct vsp1_request *req = to_vsp1_request(mreq); + struct vsp1_video *output = NULL; + struct vsp1_pipeline *pipe; + struct vsp1_video *video; + bool has_request; + unsigned int i; + + /* 1. Find the capture video node for which a buffer corresponding to + * the request has been prepared. This will be our main entry point to + * the pipeline. + * + * TODO: Fix race condition. There would be no need to lock the list + * walk as we don't add or remove video nodes at runtime. However, the + * media device is registered before the video nodes, so userspace could + * call us at probe time before all video nodes are registered. + */ + for (i = 0; i < ARRAY_SIZE(vsp1->wpf); ++i) { + if (!vsp1->wpf[i]) + continue; + + video = vsp1->wpf[i]->video; + + if (mutex_lock_interruptible(&video->lock)) + return -ERESTARTSYS; + has_request = vb2_is_streaming(&video->queue) && + vb2_queue_has_request(&video->queue, mreq->id); + mutex_unlock(&video->lock); + + if (has_request) { + /* A pipeline has a single output. */ + if (output) + return -EINVAL; + output = video; + } + } + + if (!output) + return -EINVAL; + + /* 2. Validate the pipeline. Verify streaming state, buffers and + * formats. + */ + pipe = to_vsp1_pipeline(&output->video.entity); + if (!pipe) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(pipe->inputs); ++i) { + if (!pipe->inputs[i]) + continue; + + video = pipe->inputs[i]->video; + + if (mutex_lock_interruptible(&video->lock)) + return -ERESTARTSYS; + has_request = vb2_is_streaming(&video->queue) && + vb2_queue_has_request(&video->queue, mreq->id); + mutex_unlock(&video->lock); + + if (!has_request) + return -EINVAL; + } + + /* 3. Allocate and fill the display list. */ + req->dl = vsp1_dl_list_get(pipe->output->dlm); + if (!req->dl) + return -ENOMEM; + + vsp1_pipeline_setup(pipe, req->dl, mreq); + + /* 3. Queue the request. */ + vsp1_pipeline_queue_request(pipe, req); + + return 0; +} diff --git a/drivers/media/platform/vsp1/vsp1_request.h b/drivers/media/platform/vsp1/vsp1_request.h new file mode 100644 index 000000000000..7cde04f86669 --- /dev/null +++ b/drivers/media/platform/vsp1/vsp1_request.h @@ -0,0 +1,41 @@ +/* + * vsp1_request.h -- R-Car VSP1 Request Management + * + * Copyright (C) 2015 Renesas Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@xxxxxxxxxxxxxxxx) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#ifndef __VSP1_REQUEST_H__ +#define __VSP1_REQUEST_H__ + +#include <linux/kernel.h> +#include <linux/list.h> + +struct media_device; +struct media_device_request; +struct vsp1_dl_list; + +struct vsp1_request { + struct media_device_request req; + struct vsp1_dl_list *dl; + struct list_head list; +}; + +static inline struct vsp1_request * +to_vsp1_request(struct media_device_request *req) +{ + return container_of(req, struct vsp1_request, req); +} + +struct media_device_request *vsp1_request_alloc(struct media_device *mdev); +void vsp1_request_free(struct media_device *mdev, + struct media_device_request *req); +int vsp1_request_queue(struct media_device *mdev, + struct media_device_request *req); + +#endif /* __VSP1_REQUEST_H__ */ diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index c30e29a4eb07..10bd0c0a2d94 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -32,6 +32,7 @@ #include "vsp1_dl.h" #include "vsp1_entity.h" #include "vsp1_pipe.h" +#include "vsp1_request.h" #include "vsp1_rwpf.h" #include "vsp1_uds.h" #include "vsp1_video.h" @@ -451,23 +452,44 @@ static void vsp1_video_frame_end(struct vsp1_pipeline *pipe, static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; + struct vsp1_dl_list *dl; unsigned int i; - if (!pipe->dl) - pipe->dl = vsp1_dl_list_get(pipe->output->dlm); + /* Get the display list from first available location: + * + * - the next request (when the request API is in use) + * - the pipeline if set (for the first run after streamon) + * - the request pool (for subsequent runs) + */ + dl = pipe->dl; + pipe->dl = NULL; + + if (!list_empty(&pipe->requests)) { + struct vsp1_request *req; + + req = list_first_entry(&pipe->requests, typeof(*req), list); + list_del(&req->list); + + vsp1_dl_list_put(dl); + dl = req->dl; + media_device_request_put(&req->req); + + } + + if (!dl) + dl = vsp1_dl_list_get(pipe->output->dlm); for (i = 0; i < vsp1->info->rpf_count; ++i) { struct vsp1_rwpf *rwpf = pipe->inputs[i]; if (rwpf) - vsp1_rwpf_set_memory(rwpf, pipe->dl); + vsp1_rwpf_set_memory(rwpf, dl); } if (!pipe->lif) - vsp1_rwpf_set_memory(pipe->output, pipe->dl); + vsp1_rwpf_set_memory(pipe->output, dl); - vsp1_dl_list_commit(pipe->dl); - pipe->dl = NULL; + vsp1_dl_list_commit(dl); vsp1_pipeline_run(pipe); } @@ -595,59 +617,22 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&pipe->irqlock, flags); } -static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) -{ - struct vsp1_entity *entity; - - /* Prepare the display list. */ - pipe->dl = vsp1_dl_list_get(pipe->output->dlm); - if (!pipe->dl) - return -ENOMEM; - - if (pipe->uds) { - struct vsp1_uds *uds = to_uds(&pipe->uds->subdev); - - /* If a BRU is present in the pipeline before the UDS, the alpha - * component doesn't need to be scaled as the BRU output alpha - * value is fixed to 255. Otherwise we need to scale the alpha - * component only when available at the input RPF. - */ - if (pipe->uds_input->type == VSP1_ENTITY_BRU) { - uds->scale_alpha = false; - } else { - const struct vsp1_format_info *info; - struct vsp1_rwpf *rpf = - to_rwpf(&pipe->uds_input->subdev); - - info = vsp1_get_format_info(rpf->format.pixelformat); - uds->scale_alpha = info->alpha; - } - } - - list_for_each_entry(entity, &pipe->entities, list_pipe) { - vsp1_entity_route_setup(entity, pipe->dl); - - if (entity->ops->configure) - entity->ops->configure(entity, pipe->dl, NULL); - } - - return 0; -} - static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) { struct vsp1_video *video = vb2_get_drv_priv(vq); struct vsp1_pipeline *pipe = to_vsp1_pipeline(&video->video.entity); unsigned long flags; - int ret; mutex_lock(&pipe->lock); if (pipe->stream_count == pipe->num_inputs) { - ret = vsp1_video_setup_pipeline(pipe); - if (ret < 0) { + /* Prepare the display list. */ + pipe->dl = vsp1_dl_list_get(pipe->output->dlm); + if (!pipe->dl) { mutex_unlock(&pipe->lock); - return ret; + return -ENOMEM; } + + vsp1_pipeline_setup(pipe, pipe->dl, NULL); } pipe->stream_count++; @@ -993,6 +978,7 @@ struct vsp1_video *vsp1_video_create(struct vsp1_device *vsp1, video->queue.ops = &vsp1_video_queue_qops; video->queue.mem_ops = &vb2_dma_contig_memops; video->queue.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + video->queue.allow_requests = true; ret = vb2_queue_init(&video->queue); if (ret < 0) { dev_err(video->vsp1->dev, "failed to initialize vb2 queue\n"); -- 2.4.10 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html