diff options
Diffstat (limited to 'drivers/media/platform/vsp1/vsp1_video.c')
-rw-r--r-- | drivers/media/platform/vsp1/vsp1_video.c | 220 |
1 files changed, 152 insertions, 68 deletions
diff --git a/drivers/media/platform/vsp1/vsp1_video.c b/drivers/media/platform/vsp1/vsp1_video.c index c2d3b8f0f487..81d47a09d7bc 100644 --- a/drivers/media/platform/vsp1/vsp1_video.c +++ b/drivers/media/platform/vsp1/vsp1_video.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vsp1_video.c -- R-Car VSP1 Video Node * * Copyright (C) 2013-2015 Renesas Electronics Corporation * * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) - * - * 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/list.h> @@ -28,7 +24,7 @@ #include <media/videobuf2-dma-contig.h> #include "vsp1.h" -#include "vsp1_bru.h" +#include "vsp1_brx.h" #include "vsp1_dl.h" #include "vsp1_entity.h" #include "vsp1_hgo.h" @@ -324,7 +320,7 @@ static int vsp1_video_pipeline_setup_partitions(struct vsp1_pipeline *pipe) static struct vsp1_vb2_buffer * vsp1_video_complete_buffer(struct vsp1_video *video) { - struct vsp1_pipeline *pipe = video->rwpf->pipe; + struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *next = NULL; struct vsp1_vb2_buffer *done; unsigned long flags; @@ -382,69 +378,71 @@ static void vsp1_video_pipeline_run_partition(struct vsp1_pipeline *pipe, struct vsp1_dl_list *dl, unsigned int partition) { + struct vsp1_dl_body *dlb = vsp1_dl_list_get_body0(dl); struct vsp1_entity *entity; pipe->partition = &pipe->part_table[partition]; - list_for_each_entry(entity, &pipe->entities, list_pipe) { - if (entity->ops->configure) - entity->ops->configure(entity, pipe, dl, - VSP1_ENTITY_PARAMS_PARTITION); - } + list_for_each_entry(entity, &pipe->entities, list_pipe) + vsp1_entity_configure_partition(entity, pipe, dl, dlb); } static void vsp1_video_pipeline_run(struct vsp1_pipeline *pipe) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; struct vsp1_entity *entity; + struct vsp1_dl_body *dlb; + struct vsp1_dl_list *dl; unsigned int partition; - if (!pipe->dl) - pipe->dl = vsp1_dl_list_get(pipe->output->dlm); + dl = vsp1_dl_list_get(pipe->output->dlm); /* - * Start with the runtime parameters as the configure operation can - * compute/cache information needed when configuring partitions. This - * is the case with flipping in the WPF. + * If the VSP hardware isn't configured yet (which occurs either when + * processing the first frame or after a system suspend/resume), add the + * cached stream configuration to the display list to perform a full + * initialisation. */ - list_for_each_entry(entity, &pipe->entities, list_pipe) { - if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl, - VSP1_ENTITY_PARAMS_RUNTIME); - } + if (!pipe->configured) + vsp1_dl_list_add_body(dl, pipe->stream_config); + + dlb = vsp1_dl_list_get_body0(dl); - /* Run the first partition */ - vsp1_video_pipeline_run_partition(pipe, pipe->dl, 0); + list_for_each_entry(entity, &pipe->entities, list_pipe) + vsp1_entity_configure_frame(entity, pipe, dl, dlb); - /* Process consecutive partitions as necessary */ + /* Run the first partition. */ + vsp1_video_pipeline_run_partition(pipe, dl, 0); + + /* Process consecutive partitions as necessary. */ for (partition = 1; partition < pipe->partitions; ++partition) { - struct vsp1_dl_list *dl; + struct vsp1_dl_list *dl_next; - dl = vsp1_dl_list_get(pipe->output->dlm); + dl_next = vsp1_dl_list_get(pipe->output->dlm); /* * An incomplete chain will still function, but output only * the partitions that had a dl available. The frame end * interrupt will be marked on the last dl in the chain. */ - if (!dl) { + if (!dl_next) { dev_err(vsp1->dev, "Failed to obtain a dl list. Frame will be incomplete\n"); break; } - vsp1_video_pipeline_run_partition(pipe, dl, partition); - vsp1_dl_list_add_chain(pipe->dl, dl); + vsp1_video_pipeline_run_partition(pipe, dl_next, partition); + vsp1_dl_list_add_chain(dl, dl_next); } /* Complete, and commit the head display list. */ - vsp1_dl_list_commit(pipe->dl); - pipe->dl = NULL; + vsp1_dl_list_commit(dl, false); + pipe->configured = true; vsp1_pipeline_run(pipe); } static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe, - bool completed) + unsigned int completion) { struct vsp1_device *vsp1 = pipe->output->entity.vsp1; enum vsp1_pipeline_state state; @@ -452,7 +450,7 @@ static void vsp1_video_pipeline_frame_end(struct vsp1_pipeline *pipe, unsigned int i; /* M2M Pipelines should never call here with an incomplete frame. */ - WARN_ON_ONCE(!completed); + WARN_ON_ONCE(!(completion & VSP1_DL_FRAME_END_COMPLETED)); spin_lock_irqsave(&pipe->irqlock, flags); @@ -488,7 +486,7 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe, struct media_entity_enum ent_enum; struct vsp1_entity *entity; struct media_pad *pad; - struct vsp1_bru *bru = NULL; + struct vsp1_brx *brx = NULL; int ret; ret = media_entity_enum_init(&ent_enum, &input->entity.vsp1->media_dev); @@ -524,14 +522,14 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe, if (entity->type == VSP1_ENTITY_BRU || entity->type == VSP1_ENTITY_BRS) { /* BRU and BRS can't be chained. */ - if (bru) { + if (brx) { ret = -EPIPE; goto out; } - bru = to_bru(&entity->subdev); - bru->inputs[pad->index].rpf = input; - input->bru_input = pad->index; + brx = to_brx(&entity->subdev); + brx->inputs[pad->index].rpf = input; + input->brx_input = pad->index; } /* We've reached the WPF, we're done. */ @@ -553,7 +551,7 @@ static int vsp1_video_pipeline_build_branch(struct vsp1_pipeline *pipe, } pipe->uds = entity; - pipe->uds_input = bru ? &bru->entity : &input->entity; + pipe->uds_input = brx ? &brx->entity : &input->entity; } /* Follow the source link, ignoring any HGO or HGT. */ @@ -598,20 +596,19 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, subdev = media_entity_to_v4l2_subdev(entity); e = to_vsp1_entity(subdev); list_add_tail(&e->list_pipe, &pipe->entities); + e->pipe = pipe; switch (e->type) { case VSP1_ENTITY_RPF: rwpf = to_rwpf(subdev); pipe->inputs[rwpf->entity.index] = rwpf; rwpf->video->pipe_index = ++pipe->num_inputs; - rwpf->pipe = pipe; break; case VSP1_ENTITY_WPF: rwpf = to_rwpf(subdev); pipe->output = rwpf; rwpf->video->pipe_index = 0; - rwpf->pipe = pipe; break; case VSP1_ENTITY_LIF: @@ -620,17 +617,15 @@ static int vsp1_video_pipeline_build(struct vsp1_pipeline *pipe, case VSP1_ENTITY_BRU: case VSP1_ENTITY_BRS: - pipe->bru = e; + pipe->brx = e; break; case VSP1_ENTITY_HGO: pipe->hgo = e; - to_hgo(subdev)->histo.pipe = pipe; break; case VSP1_ENTITY_HGT: pipe->hgt = e; - to_hgt(subdev)->histo.pipe = pipe; break; default: @@ -682,7 +677,7 @@ static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video) * Otherwise allocate a new pipeline and initialize it, it will be freed * when the last reference is released. */ - if (!video->rwpf->pipe) { + if (!video->rwpf->entity.pipe) { pipe = kzalloc(sizeof(*pipe), GFP_KERNEL); if (!pipe) return ERR_PTR(-ENOMEM); @@ -694,7 +689,7 @@ static struct vsp1_pipeline *vsp1_video_pipeline_get(struct vsp1_video *video) return ERR_PTR(ret); } } else { - pipe = video->rwpf->pipe; + pipe = video->rwpf->entity.pipe; kref_get(&pipe->kref); } @@ -777,7 +772,7 @@ static void vsp1_video_buffer_queue(struct vb2_buffer *vb) { struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); struct vsp1_video *video = vb2_get_drv_priv(vb->vb2_queue); - struct vsp1_pipeline *pipe = video->rwpf->pipe; + struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; struct vsp1_vb2_buffer *buf = to_vsp1_vb2_buffer(vbuf); unsigned long flags; bool empty; @@ -812,11 +807,6 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) if (ret < 0) return ret; - /* 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); @@ -838,20 +828,25 @@ static int vsp1_video_setup_pipeline(struct vsp1_pipeline *pipe) } } - list_for_each_entry(entity, &pipe->entities, list_pipe) { - vsp1_entity_route_setup(entity, pipe, pipe->dl); + /* + * Compute and cache the stream configuration into a body. The cached + * body will be added to the display list by vsp1_video_pipeline_run() + * whenever the pipeline needs to be fully reconfigured. + */ + pipe->stream_config = vsp1_dlm_dl_body_get(pipe->output->dlm); + if (!pipe->stream_config) + return -ENOMEM; - if (entity->ops->configure) - entity->ops->configure(entity, pipe, pipe->dl, - VSP1_ENTITY_PARAMS_INIT); + list_for_each_entry(entity, &pipe->entities, list_pipe) { + vsp1_entity_route_setup(entity, pipe, pipe->stream_config); + vsp1_entity_configure_stream(entity, pipe, pipe->stream_config); } return 0; } -static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe) +static void vsp1_video_release_buffers(struct vsp1_video *video) { - struct vsp1_video *video = pipe->output->video; struct vsp1_vb2_buffer *buffer; unsigned long flags; @@ -861,18 +856,26 @@ static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe) vb2_buffer_done(&buffer->buf.vb2_buf, VB2_BUF_STATE_ERROR); INIT_LIST_HEAD(&video->irqqueue); spin_unlock_irqrestore(&video->irqlock, flags); +} + +static void vsp1_video_cleanup_pipeline(struct vsp1_pipeline *pipe) +{ + lockdep_assert_held(&pipe->lock); + + /* Release any cached configuration from our output video. */ + vsp1_dl_body_put(pipe->stream_config); + pipe->stream_config = NULL; + pipe->configured = false; /* Release our partition table allocation */ - mutex_lock(&pipe->lock); kfree(pipe->part_table); pipe->part_table = NULL; - mutex_unlock(&pipe->lock); } 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 = video->rwpf->pipe; + struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; bool start_pipeline = false; unsigned long flags; int ret; @@ -881,8 +884,9 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) if (pipe->stream_count == pipe->num_inputs) { ret = vsp1_video_setup_pipeline(pipe); if (ret < 0) { - mutex_unlock(&pipe->lock); + vsp1_video_release_buffers(video); vsp1_video_cleanup_pipeline(pipe); + mutex_unlock(&pipe->lock); return ret; } @@ -913,7 +917,7 @@ static int vsp1_video_start_streaming(struct vb2_queue *vq, unsigned int count) static void vsp1_video_stop_streaming(struct vb2_queue *vq) { struct vsp1_video *video = vb2_get_drv_priv(vq); - struct vsp1_pipeline *pipe = video->rwpf->pipe; + struct vsp1_pipeline *pipe = video->rwpf->entity.pipe; unsigned long flags; int ret; @@ -932,13 +936,12 @@ static void vsp1_video_stop_streaming(struct vb2_queue *vq) if (ret == -ETIMEDOUT) dev_err(video->vsp1->dev, "pipeline stop timeout\n"); - vsp1_dl_list_put(pipe->dl); - pipe->dl = NULL; + vsp1_video_cleanup_pipeline(pipe); } mutex_unlock(&pipe->lock); media_pipeline_stop(&video->video.entity); - vsp1_video_cleanup_pipeline(pipe); + vsp1_video_release_buffers(video); vsp1_video_pipeline_put(pipe); } @@ -1173,6 +1176,87 @@ static const struct v4l2_file_operations vsp1_video_fops = { }; /* ----------------------------------------------------------------------------- + * Suspend and Resume + */ + +void vsp1_video_suspend(struct vsp1_device *vsp1) +{ + unsigned long flags; + unsigned int i; + int ret; + + /* + * To avoid increasing the system suspend time needlessly, loop over the + * pipelines twice, first to set them all to the stopping state, and + * then to wait for the stop to complete. + */ + for (i = 0; i < vsp1->info->wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = wpf->entity.pipe; + if (pipe == NULL) + continue; + + spin_lock_irqsave(&pipe->irqlock, flags); + if (pipe->state == VSP1_PIPELINE_RUNNING) + pipe->state = VSP1_PIPELINE_STOPPING; + spin_unlock_irqrestore(&pipe->irqlock, flags); + } + + for (i = 0; i < vsp1->info->wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = wpf->entity.pipe; + if (pipe == NULL) + continue; + + ret = wait_event_timeout(pipe->wq, vsp1_pipeline_stopped(pipe), + msecs_to_jiffies(500)); + if (ret == 0) + dev_warn(vsp1->dev, "pipeline %u stop timeout\n", + wpf->entity.index); + } +} + +void vsp1_video_resume(struct vsp1_device *vsp1) +{ + unsigned long flags; + unsigned int i; + + /* Resume all running pipelines. */ + for (i = 0; i < vsp1->info->wpf_count; ++i) { + struct vsp1_rwpf *wpf = vsp1->wpf[i]; + struct vsp1_pipeline *pipe; + + if (wpf == NULL) + continue; + + pipe = wpf->entity.pipe; + if (pipe == NULL) + continue; + + /* + * The hardware may have been reset during a suspend and will + * need a full reconfiguration. + */ + pipe->configured = false; + + spin_lock_irqsave(&pipe->irqlock, flags); + if (vsp1_pipeline_ready(pipe)) + vsp1_video_pipeline_run(pipe); + spin_unlock_irqrestore(&pipe->irqlock, flags); + } +} + +/* ----------------------------------------------------------------------------- * Initialization and Cleanup */ |