summaryrefslogtreecommitdiff
path: root/drivers/media/platform/raspberrypi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/raspberrypi')
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/Kconfig1
-rw-r--r--drivers/media/platform/raspberrypi/pisp_be/pisp_be.c207
-rw-r--r--drivers/media/platform/raspberrypi/rp1-cfe/cfe.c4
3 files changed, 106 insertions, 106 deletions
diff --git a/drivers/media/platform/raspberrypi/pisp_be/Kconfig b/drivers/media/platform/raspberrypi/pisp_be/Kconfig
index 46765a2e4c4d..a9e51fd94aad 100644
--- a/drivers/media/platform/raspberrypi/pisp_be/Kconfig
+++ b/drivers/media/platform/raspberrypi/pisp_be/Kconfig
@@ -3,6 +3,7 @@ config VIDEO_RASPBERRYPI_PISP_BE
depends on V4L_PLATFORM_DRIVERS
depends on VIDEO_DEV
depends on ARCH_BCM2835 || COMPILE_TEST
+ depends on PM
select VIDEO_V4L2_SUBDEV_API
select MEDIA_CONTROLLER
select VIDEOBUF2_DMA_CONTIG
diff --git a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
index 7596ae1f7de6..b30891718d8d 100644
--- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
+++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
@@ -9,9 +9,11 @@
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/lockdep.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-dma-contig.h>
@@ -161,8 +163,6 @@ struct pispbe_node {
struct mutex node_lock;
/* vb2_queue lock */
struct mutex queue_lock;
- /* Protect pispbe_node->ready_queue and pispbe_buffer->ready_list */
- spinlock_t ready_lock;
struct list_head ready_queue;
struct vb2_queue queue;
struct v4l2_format format;
@@ -190,6 +190,8 @@ struct pispbe_hw_enables {
/* Records a job configuration and memory addresses. */
struct pispbe_job_descriptor {
+ struct list_head queue;
+ struct pispbe_buffer *buffers[PISPBE_NUM_NODES];
dma_addr_t hw_dma_addrs[N_HW_ADDRESSES];
struct pisp_be_tiles_config *config;
struct pispbe_hw_enables hw_enables;
@@ -215,8 +217,10 @@ struct pispbe_dev {
unsigned int sequence;
u32 streaming_map;
struct pispbe_job queued_job, running_job;
- spinlock_t hw_lock; /* protects "hw_busy" flag and streaming_map */
+ /* protects "hw_busy" flag, streaming_map and job_queue */
+ spinlock_t hw_lock;
bool hw_busy; /* non-zero if a job is queued or is being started */
+ struct list_head job_queue;
int irq;
u32 hw_version;
u8 done, started;
@@ -368,10 +372,7 @@ static void pispbe_xlate_addrs(struct pispbe_dev *pispbe,
ret = pispbe_get_planes_addr(addrs, buf[MAIN_INPUT_NODE],
&pispbe->node[MAIN_INPUT_NODE]);
if (ret <= 0) {
- /*
- * This shouldn't happen; pispbe_schedule_internal should insist
- * on an input.
- */
+ /* Shouldn't happen, we have validated an input is available. */
dev_warn(pispbe->dev, "ISP-BE missing input\n");
hw_en->bayer_enables = 0;
hw_en->rgb_enables = 0;
@@ -443,42 +444,48 @@ static void pispbe_xlate_addrs(struct pispbe_dev *pispbe,
* For Output0, Output1, Tdn and Stitch, a buffer only needs to be
* available if the blocks are enabled in the config.
*
- * Needs to be called with hw_lock held.
+ * If all the buffers required to form a job are available, append the
+ * job descriptor to the job queue to be later queued to the HW.
*
* Returns 0 if a job has been successfully prepared, < 0 otherwise.
*/
-static int pispbe_prepare_job(struct pispbe_dev *pispbe,
- struct pispbe_job_descriptor *job)
+static int pispbe_prepare_job(struct pispbe_dev *pispbe)
{
+ struct pispbe_job_descriptor __free(kfree) *job = NULL;
struct pispbe_buffer *buf[PISPBE_NUM_NODES] = {};
+ unsigned int streaming_map;
unsigned int config_index;
struct pispbe_node *node;
- unsigned long flags;
- lockdep_assert_held(&pispbe->hw_lock);
+ lockdep_assert_irqs_enabled();
- memset(job, 0, sizeof(struct pispbe_job_descriptor));
+ scoped_guard(spinlock_irq, &pispbe->hw_lock) {
+ static const u32 mask = BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE);
- if (((BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)) &
- pispbe->streaming_map) !=
- (BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)))
- return -ENODEV;
+ if ((pispbe->streaming_map & mask) != mask)
+ return -ENODEV;
+
+ /*
+ * Take a copy of streaming_map: nodes activated after this
+ * point are ignored when preparing this job.
+ */
+ streaming_map = pispbe->streaming_map;
+ }
+
+ job = kzalloc(sizeof(*job), GFP_KERNEL);
+ if (!job)
+ return -ENOMEM;
node = &pispbe->node[CONFIG_NODE];
- spin_lock_irqsave(&node->ready_lock, flags);
buf[CONFIG_NODE] = list_first_entry_or_null(&node->ready_queue,
struct pispbe_buffer,
ready_list);
- if (buf[CONFIG_NODE]) {
- list_del(&buf[CONFIG_NODE]->ready_list);
- pispbe->queued_job.buf[CONFIG_NODE] = buf[CONFIG_NODE];
- }
- spin_unlock_irqrestore(&node->ready_lock, flags);
-
- /* Exit early if no config buffer has been queued. */
if (!buf[CONFIG_NODE])
return -ENODEV;
+ list_del(&buf[CONFIG_NODE]->ready_list);
+ job->buffers[CONFIG_NODE] = buf[CONFIG_NODE];
+
config_index = buf[CONFIG_NODE]->vb.vb2_buf.index;
job->config = &pispbe->config[config_index];
job->tiles = pispbe->config_dma_addr +
@@ -498,7 +505,7 @@ static int pispbe_prepare_job(struct pispbe_dev *pispbe,
continue;
buf[i] = NULL;
- if (!(pispbe->streaming_map & BIT(i)))
+ if (!(streaming_map & BIT(i)))
continue;
if ((!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT0) &&
@@ -525,25 +532,28 @@ static int pispbe_prepare_job(struct pispbe_dev *pispbe,
node = &pispbe->node[i];
/* Pull a buffer from each V4L2 queue to form the queued job */
- spin_lock_irqsave(&node->ready_lock, flags);
buf[i] = list_first_entry_or_null(&node->ready_queue,
struct pispbe_buffer,
ready_list);
if (buf[i]) {
list_del(&buf[i]->ready_list);
- pispbe->queued_job.buf[i] = buf[i];
+ job->buffers[i] = buf[i];
}
- spin_unlock_irqrestore(&node->ready_lock, flags);
if (!buf[i] && !ignore_buffers)
goto err_return_buffers;
}
- pispbe->queued_job.valid = true;
-
/* Convert buffers to DMA addresses for the hardware */
pispbe_xlate_addrs(pispbe, job, buf);
+ scoped_guard(spinlock_irq, &pispbe->hw_lock) {
+ list_add_tail(&job->queue, &pispbe->job_queue);
+ }
+
+ /* Set job to NULL to avoid automatic release due to __free(). */
+ job = NULL;
+
return 0;
err_return_buffers:
@@ -554,33 +564,37 @@ err_return_buffers:
continue;
/* Return the buffer to the ready_list queue */
- spin_lock_irqsave(&n->ready_lock, flags);
list_add(&buf[i]->ready_list, &n->ready_queue);
- spin_unlock_irqrestore(&n->ready_lock, flags);
}
- memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job));
-
return -ENODEV;
}
static void pispbe_schedule(struct pispbe_dev *pispbe, bool clear_hw_busy)
{
- struct pispbe_job_descriptor job;
- unsigned long flags;
- int ret;
+ struct pispbe_job_descriptor *job;
- spin_lock_irqsave(&pispbe->hw_lock, flags);
+ scoped_guard(spinlock_irqsave, &pispbe->hw_lock) {
+ if (clear_hw_busy)
+ pispbe->hw_busy = false;
- if (clear_hw_busy)
- pispbe->hw_busy = false;
+ if (pispbe->hw_busy)
+ return;
- if (pispbe->hw_busy)
- goto unlock_and_return;
+ job = list_first_entry_or_null(&pispbe->job_queue,
+ struct pispbe_job_descriptor,
+ queue);
+ if (!job)
+ return;
- ret = pispbe_prepare_job(pispbe, &job);
- if (ret)
- goto unlock_and_return;
+ list_del(&job->queue);
+
+ for (unsigned int i = 0; i < PISPBE_NUM_NODES; i++)
+ pispbe->queued_job.buf[i] = job->buffers[i];
+ pispbe->queued_job.valid = true;
+
+ pispbe->hw_busy = true;
+ }
/*
* We can kick the job off without the hw_lock, as this can
@@ -588,34 +602,8 @@ static void pispbe_schedule(struct pispbe_dev *pispbe, bool clear_hw_busy)
* only when the following job has been queued and an interrupt
* is rised.
*/
- pispbe->hw_busy = true;
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
-
- if (job.config->num_tiles <= 0 ||
- job.config->num_tiles > PISP_BACK_END_NUM_TILES ||
- !((job.hw_enables.bayer_enables | job.hw_enables.rgb_enables) &
- PISP_BE_BAYER_ENABLE_INPUT)) {
- /*
- * Bad job. We can't let it proceed as it could lock up
- * the hardware, or worse!
- *
- * For now, just force num_tiles to 0, which causes the
- * H/W to do something bizarre but survivable. It
- * increments (started,done) counters by more than 1,
- * but we seem to survive...
- */
- dev_dbg(pispbe->dev, "Bad job: invalid number of tiles: %u\n",
- job.config->num_tiles);
- job.config->num_tiles = 0;
- }
-
- pispbe_queue_job(pispbe, &job);
-
- return;
-
-unlock_and_return:
- /* No job has been queued, just release the lock and return. */
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+ pispbe_queue_job(pispbe, job);
+ kfree(job);
}
static void pispbe_isr_jobdone(struct pispbe_dev *pispbe,
@@ -706,6 +694,13 @@ static int pisp_be_validate_config(struct pispbe_dev *pispbe,
return -EIO;
}
+ if (config->num_tiles == 0 ||
+ config->num_tiles > PISP_BACK_END_NUM_TILES) {
+ dev_dbg(dev, "%s: Invalid number of tiles: %d\n", __func__,
+ config->num_tiles);
+ return -EINVAL;
+ }
+
/* Ensure output config strides and buffer sizes match the V4L2 formats. */
fmt = &pispbe->node[TDN_OUTPUT_NODE].format;
if (bayer_enables & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) {
@@ -860,18 +855,16 @@ static void pispbe_node_buffer_queue(struct vb2_buffer *buf)
container_of(vbuf, struct pispbe_buffer, vb);
struct pispbe_node *node = vb2_get_drv_priv(buf->vb2_queue);
struct pispbe_dev *pispbe = node->pispbe;
- unsigned long flags;
dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node));
- spin_lock_irqsave(&node->ready_lock, flags);
list_add_tail(&buffer->ready_list, &node->ready_queue);
- spin_unlock_irqrestore(&node->ready_lock, flags);
/*
* Every time we add a buffer, check if there's now some work for the hw
* to do.
*/
- pispbe_schedule(pispbe, false);
+ if (!pispbe_prepare_job(pispbe))
+ pispbe_schedule(pispbe, false);
}
static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
@@ -879,17 +872,16 @@ static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
struct pispbe_node *node = vb2_get_drv_priv(q);
struct pispbe_dev *pispbe = node->pispbe;
struct pispbe_buffer *buf, *tmp;
- unsigned long flags;
int ret;
ret = pm_runtime_resume_and_get(pispbe->dev);
if (ret < 0)
goto err_return_buffers;
- spin_lock_irqsave(&pispbe->hw_lock, flags);
- node->pispbe->streaming_map |= BIT(node->id);
- node->pispbe->sequence = 0;
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+ scoped_guard(spinlock_irq, &pispbe->hw_lock) {
+ node->pispbe->streaming_map |= BIT(node->id);
+ node->pispbe->sequence = 0;
+ }
dev_dbg(pispbe->dev, "%s: for node %s (count %u)\n",
__func__, NODE_NAME(node), count);
@@ -897,17 +889,16 @@ static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
node->pispbe->streaming_map);
/* Maybe we're ready to run. */
- pispbe_schedule(pispbe, false);
+ if (!pispbe_prepare_job(pispbe))
+ pispbe_schedule(pispbe, false);
return 0;
err_return_buffers:
- spin_lock_irqsave(&pispbe->hw_lock, flags);
list_for_each_entry_safe(buf, tmp, &node->ready_queue, ready_list) {
list_del(&buf->ready_list);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED);
}
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
return ret;
}
@@ -916,8 +907,9 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q)
{
struct pispbe_node *node = vb2_get_drv_priv(q);
struct pispbe_dev *pispbe = node->pispbe;
+ struct pispbe_job_descriptor *job, *temp;
struct pispbe_buffer *buf;
- unsigned long flags;
+ LIST_HEAD(tmp_list);
/*
* Now this is a bit awkward. In a simple M2M device we could just wait
@@ -929,11 +921,7 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q)
* This may return buffers out of order.
*/
dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node));
- spin_lock_irqsave(&pispbe->hw_lock, flags);
do {
- unsigned long flags1;
-
- spin_lock_irqsave(&node->ready_lock, flags1);
buf = list_first_entry_or_null(&node->ready_queue,
struct pispbe_buffer,
ready_list);
@@ -941,15 +929,26 @@ static void pispbe_node_stop_streaming(struct vb2_queue *q)
list_del(&buf->ready_list);
vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
}
- spin_unlock_irqrestore(&node->ready_lock, flags1);
} while (buf);
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
vb2_wait_for_all_buffers(&node->queue);
- spin_lock_irqsave(&pispbe->hw_lock, flags);
+ spin_lock_irq(&pispbe->hw_lock);
pispbe->streaming_map &= ~BIT(node->id);
- spin_unlock_irqrestore(&pispbe->hw_lock, flags);
+
+ if (pispbe->streaming_map == 0) {
+ /*
+ * If all nodes have stopped streaming release all jobs
+ * without holding the lock.
+ */
+ list_splice_init(&pispbe->job_queue, &tmp_list);
+ }
+ spin_unlock_irq(&pispbe->hw_lock);
+
+ list_for_each_entry_safe(job, temp, &tmp_list, queue) {
+ list_del(&job->queue);
+ kfree(job);
+ }
pm_runtime_mark_last_busy(pispbe->dev);
pm_runtime_put_autosuspend(pispbe->dev);
@@ -1114,10 +1113,12 @@ static void pispbe_try_format(struct v4l2_format *f, struct pispbe_node *node)
f->fmt.pix_mp.pixelformat = fmt->fourcc;
f->fmt.pix_mp.num_planes = fmt->num_planes;
f->fmt.pix_mp.field = V4L2_FIELD_NONE;
- f->fmt.pix_mp.width = max(min(f->fmt.pix_mp.width, 65536u),
- PISP_BACK_END_MIN_TILE_WIDTH);
- f->fmt.pix_mp.height = max(min(f->fmt.pix_mp.height, 65536u),
- PISP_BACK_END_MIN_TILE_HEIGHT);
+ f->fmt.pix_mp.width = clamp(f->fmt.pix_mp.width,
+ PISP_BACK_END_MIN_TILE_WIDTH,
+ PISP_BACK_END_MAX_TILE_WIDTH);
+ f->fmt.pix_mp.height = clamp(f->fmt.pix_mp.height,
+ PISP_BACK_END_MIN_TILE_HEIGHT,
+ PISP_BACK_END_MAX_TILE_HEIGHT);
/*
* Fill in the actual colour space when the requested one was
@@ -1407,7 +1408,6 @@ static int pispbe_init_node(struct pispbe_dev *pispbe, unsigned int id)
mutex_init(&node->node_lock);
mutex_init(&node->queue_lock);
INIT_LIST_HEAD(&node->ready_queue);
- spin_lock_init(&node->ready_lock);
node->format.type = node->buf_type;
pispbe_node_def_fmt(node);
@@ -1691,6 +1691,8 @@ static int pispbe_probe(struct platform_device *pdev)
if (!pispbe)
return -ENOMEM;
+ INIT_LIST_HEAD(&pispbe->job_queue);
+
dev_set_drvdata(&pdev->dev, pispbe);
pispbe->dev = &pdev->dev;
platform_set_drvdata(pdev, pispbe);
@@ -1726,7 +1728,7 @@ static int pispbe_probe(struct platform_device *pdev)
pm_runtime_use_autosuspend(pispbe->dev);
pm_runtime_enable(pispbe->dev);
- ret = pispbe_runtime_resume(pispbe->dev);
+ ret = pm_runtime_resume_and_get(pispbe->dev);
if (ret)
goto pm_runtime_disable_err;
@@ -1748,7 +1750,7 @@ static int pispbe_probe(struct platform_device *pdev)
disable_devs_err:
pispbe_destroy_devices(pispbe);
pm_runtime_suspend_err:
- pispbe_runtime_suspend(pispbe->dev);
+ pm_runtime_put(pispbe->dev);
pm_runtime_disable_err:
pm_runtime_dont_use_autosuspend(pispbe->dev);
pm_runtime_disable(pispbe->dev);
@@ -1762,7 +1764,6 @@ static void pispbe_remove(struct platform_device *pdev)
pispbe_destroy_devices(pispbe);
- pispbe_runtime_suspend(pispbe->dev);
pm_runtime_dont_use_autosuspend(pispbe->dev);
pm_runtime_disable(pispbe->dev);
}
diff --git a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
index fcadb2143c88..62dca76b468d 100644
--- a/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
+++ b/drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
@@ -1024,9 +1024,6 @@ static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
cfe_dbg(cfe, "%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
node->buffer_queue.type);
- if (vq->max_num_buffers + *nbuffers < 3)
- *nbuffers = 3 - vq->max_num_buffers;
-
if (*nplanes) {
if (sizes[0] < size) {
cfe_err(cfe, "sizes[0] %i < size %u\n", sizes[0], size);
@@ -1998,6 +1995,7 @@ static int cfe_register_node(struct cfe_device *cfe, int id)
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &node->lock;
q->min_queued_buffers = 1;
+ q->min_reqbufs_allocation = 3;
q->dev = &cfe->pdev->dev;
ret = vb2_queue_init(q);