summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorBeata Michalska <b.michalska@samsung.com>2014-07-23 15:34:25 +0200
committerSylwester Nawrocki <s.nawrocki@samsung.com>2014-11-27 03:42:53 -0800
commitcd6e2e406309601c9452f5132b5b8e544aa2a37c (patch)
tree246141063ba3d7777ba5c09274de72ba8556610d /drivers
parent9cff26ea83556e8e899dc7844ec662c30e203d7d (diff)
downloadlinux-3.10-cd6e2e406309601c9452f5132b5b8e544aa2a37c.tar.gz
linux-3.10-cd6e2e406309601c9452f5132b5b8e544aa2a37c.tar.bz2
linux-3.10-cd6e2e406309601c9452f5132b5b8e544aa2a37c.zip
exynos: fimc-is: Internal buffer list cleanup
As the driver maintaines separate buffer lists per each video device which are beyond control of v4l2/vb2 framework, those lists should be properly handled and cleared once the buffers are no longer available. The changes introduced allow controlling state of each queued buffer and removing it from internal buffer lists when needed. Signed-off-by: Beata Michalska <b.michalska@samsung.com> Change-Id: I4dea7df3910d6836edee1387c5a1147649043b81
Diffstat (limited to 'drivers')
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-core.h9
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-isp.c34
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-pipeline.c6
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is-scaler.c44
-rw-r--r--drivers/media/platform/exynos5-is/fimc-is.h38
5 files changed, 113 insertions, 18 deletions
diff --git a/drivers/media/platform/exynos5-is/fimc-is-core.h b/drivers/media/platform/exynos5-is/fimc-is-core.h
index 1e8abe3588d..b9478a2b76a 100644
--- a/drivers/media/platform/exynos5-is/fimc-is-core.h
+++ b/drivers/media/platform/exynos5-is/fimc-is-core.h
@@ -90,8 +90,17 @@ enum fimc_is_sensor_pos {
SENSOR_CAM1
};
+enum fimc_is_buf_state {
+ FIMC_IS_BUF_IDLE,
+ FIMC_IS_BUF_QUEUED,
+ FIMC_IS_BUF_ACTIVE,
+ FIMC_IS_BUF_DONE,
+ FIMC_IS_BUF_INVALID,
+};
+
struct fimc_is_buf {
struct vb2_buffer vb;
+ unsigned int state;
struct list_head list;
unsigned int paddr[FIMC_IS_MAX_PLANES];
};
diff --git a/drivers/media/platform/exynos5-is/fimc-is-isp.c b/drivers/media/platform/exynos5-is/fimc-is-isp.c
index 82485d651bd..082fe7db53b 100644
--- a/drivers/media/platform/exynos5-is/fimc-is-isp.c
+++ b/drivers/media/platform/exynos5-is/fimc-is-isp.c
@@ -63,15 +63,18 @@ static void isp_video_output_stop_streaming(struct vb2_queue *vq)
struct fimc_is_buf *buf;
/* Release unused buffers */
+ fimc_is_pipeline_buf_lock(isp->pipeline);
while (!list_empty(&isp->wait_queue)) {
buf = fimc_is_isp_wait_queue_get(isp);
+ buf->state = FIMC_IS_BUF_INVALID;
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
while (!list_empty(&isp->run_queue)) {
buf = fimc_is_isp_run_queue_get(isp);
+ buf->state = FIMC_IS_BUF_INVALID;
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
-
+ fimc_is_pipeline_buf_unlock(isp->pipeline);
clear_bit(STATE_RUNNING, &isp->output_state);
return;
}
@@ -103,6 +106,7 @@ static int isp_video_output_buffer_init(struct vb2_buffer *vb)
struct fimc_is_buf *buf = container_of(vb, struct fimc_is_buf, vb);
buf->paddr[0] = vb2_dma_contig_plane_dma_addr(vb, 0);
+ buf->state = FIMC_IS_BUF_IDLE;
return 0;
}
@@ -136,6 +140,33 @@ static void isp_video_output_buffer_queue(struct vb2_buffer *vb)
fimc_is_pipeline_shot_safe(isp->pipeline);
}
+static void isp_video_output_buffer_cleanup(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct fimc_is_isp *isp = vb2_get_drv_priv(vq);
+ struct fimc_is_buf *fbuf = container_of(vb, struct fimc_is_buf, vb);
+
+ /*
+ * Skip the cleanup case the buffer
+ * is no longer accessible through any of the queues
+ * or has not been queued at all.
+ */
+ if (fbuf->state == FIMC_IS_BUF_IDLE ||
+ fbuf->state == FIMC_IS_BUF_INVALID ||
+ fbuf->state == FIMC_IS_BUF_DONE)
+ return;
+
+ fimc_is_pipeline_buf_lock(isp->pipeline);
+ list_del(&fbuf->list);
+ if (fbuf->state == FIMC_IS_BUF_QUEUED)
+ --isp->wait_queue_cnt;
+ else /* FIMC_IS_BUF_ACTIVE */
+ --isp->run_queue_cnt;
+ fbuf->state = FIMC_IS_BUF_INVALID;
+ fimc_is_pipeline_buf_unlock(isp->pipeline);
+
+}
+
static const struct vb2_ops isp_video_output_qops = {
.queue_setup = isp_video_output_queue_setup,
.buf_init = isp_video_output_buffer_init,
@@ -143,6 +174,7 @@ static const struct vb2_ops isp_video_output_qops = {
.buf_queue = isp_video_output_buffer_queue,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
+ .buf_cleanup = isp_video_output_buffer_cleanup,
.start_streaming = isp_video_output_start_streaming,
.stop_streaming = isp_video_output_stop_streaming,
};
diff --git a/drivers/media/platform/exynos5-is/fimc-is-pipeline.c b/drivers/media/platform/exynos5-is/fimc-is-pipeline.c
index bc87a59ab03..be9623644f8 100644
--- a/drivers/media/platform/exynos5-is/fimc-is-pipeline.c
+++ b/drivers/media/platform/exynos5-is/fimc-is-pipeline.c
@@ -1535,12 +1535,14 @@ static void fimc_is_pipeline_shot_cancel(struct fimc_is_pipeline *pipeline)
/* Release ISP buffers */
while (!list_empty(&isp->wait_queue)) {
buffer = fimc_is_isp_wait_queue_get(isp);
+ buffer->state = FIMC_IS_BUF_INVALID;
vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR);
}
if(!list_empty(&isp->run_queue)) {
while (!list_empty(&isp->run_queue)) {
buffer = fimc_is_isp_run_queue_get(isp);
+ buffer->state = FIMC_IS_BUF_INVALID;
vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR);
}
}
@@ -1549,10 +1551,12 @@ static void fimc_is_pipeline_shot_cancel(struct fimc_is_pipeline *pipeline)
if (test_bit(COMP_START, &pipeline->subip_state[IS_SCC])) {
while (!list_empty(&scaler->wait_queue)) {
buffer = fimc_is_scaler_wait_queue_get(scaler);
+ buffer->state = FIMC_IS_BUF_INVALID;
vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR);
}
while (!list_empty(&scaler->run_queue)) {
buffer = fimc_is_scaler_run_queue_get(scaler);
+ buffer->state = FIMC_IS_BUF_INVALID;
vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR);
}
}
@@ -1560,10 +1564,12 @@ static void fimc_is_pipeline_shot_cancel(struct fimc_is_pipeline *pipeline)
if (test_bit(COMP_START, &pipeline->subip_state[IS_SCP])) {
while (!list_empty(&scaler->wait_queue)) {
buffer = fimc_is_scaler_wait_queue_get(scaler);
+ buffer->state = FIMC_IS_BUF_INVALID;
vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR);
}
while (!list_empty(&scaler->run_queue)) {
buffer = fimc_is_scaler_run_queue_get(scaler);
+ buffer->state = FIMC_IS_BUF_INVALID;
vb2_buffer_done(&buffer->vb, VB2_BUF_STATE_ERROR);
}
}
diff --git a/drivers/media/platform/exynos5-is/fimc-is-scaler.c b/drivers/media/platform/exynos5-is/fimc-is-scaler.c
index 25f801d644d..966ccf0fdf6 100644
--- a/drivers/media/platform/exynos5-is/fimc-is-scaler.c
+++ b/drivers/media/platform/exynos5-is/fimc-is-scaler.c
@@ -74,19 +74,26 @@ static void scaler_video_capture_stop_streaming(struct vb2_queue *vq)
struct fimc_is_buf *buf;
int ret;
- ret = fimc_is_pipeline_scaler_stop(ctx->pipeline, ctx->scaler_id);
- if (ret)
- v4l2_info(&ctx->subdev, "Scaler already stopped.\n");
+ if (!test_bit(STATE_RUNNING, &ctx->capture_state))
+ return;
+
+ ret = fimc_is_pipeline_scaler_stop(ctx->pipeline, ctx->scaler_id);
+ if (ret)
+ v4l2_info(&ctx->subdev, "Scaler already stopped.\n");
/* Release un-used buffers */
+ fimc_is_pipeline_buf_lock(ctx->pipeline);
while (!list_empty(&ctx->wait_queue)) {
buf = fimc_is_scaler_wait_queue_get(ctx);
+ buf->state = FIMC_IS_BUF_INVALID;
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
while (!list_empty(&ctx->run_queue)) {
buf = fimc_is_scaler_run_queue_get(ctx);
+ buf->state = FIMC_IS_BUF_INVALID;
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
}
+ fimc_is_pipeline_buf_unlock(ctx->pipeline);
clear_bit(STATE_RUNNING, &ctx->capture_state);
}
@@ -125,7 +132,7 @@ static int scaler_video_capture_buffer_init(struct vb2_buffer *vb)
fmt = ctx->fmt;
for (i = 0; i < fmt->num_planes; i++)
buf->paddr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
-
+ buf->state = FIMC_IS_BUF_IDLE;
return 0;
}
@@ -163,6 +170,32 @@ static void scaler_video_capture_buffer_queue(struct vb2_buffer *vb)
fimc_is_pipeline_buf_unlock(ctx->pipeline);
}
+static void scaler_video_capture_buffer_cleanup(struct vb2_buffer *vb)
+{
+ struct vb2_queue *vq = vb->vb2_queue;
+ struct fimc_is_scaler *ctx = vb2_get_drv_priv(vq);
+ struct fimc_is_buf *fbuf = container_of(vb, struct fimc_is_buf, vb);
+
+ /*
+ * Skip the cleanup case the buffer
+ * is no longer accessible through any of the queues
+ * or has not been queued at all.
+ */
+ if (fbuf->state == FIMC_IS_BUF_IDLE ||
+ fbuf->state == FIMC_IS_BUF_INVALID ||
+ fbuf->state == FIMC_IS_BUF_DONE)
+ return;
+
+ fimc_is_pipeline_buf_lock(ctx->pipeline);
+ list_del(&fbuf->list);
+ if (fbuf->state == FIMC_IS_BUF_QUEUED)
+ --ctx->wait_queue_cnt;
+ else /* FIMC_IS_BUF_ACTIVE */
+ --ctx->run_queue_cnt;
+ fbuf->state = FIMC_IS_BUF_INVALID;
+ fimc_is_pipeline_buf_unlock(ctx->pipeline);
+}
+
static const struct vb2_ops scaler_video_capture_qops = {
.queue_setup = scaler_video_capture_queue_setup,
.buf_init = scaler_video_capture_buffer_init,
@@ -170,6 +203,7 @@ static const struct vb2_ops scaler_video_capture_qops = {
.buf_queue = scaler_video_capture_buffer_queue,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
+ .buf_cleanup = scaler_video_capture_buffer_cleanup,
.start_streaming = scaler_video_capture_start_streaming,
.stop_streaming = scaler_video_capture_stop_streaming,
};
@@ -336,7 +370,7 @@ static int scaler_reqbufs(struct file *file, void *priv,
if (reqbufs->count < FIMC_IS_SCALER_REQ_BUFS_MIN) {
reqbufs->count = 0;
- vb2_reqbufs(&ctx->vbq, reqbufs);
+ vb2_reqbufs(&ctx->vbq, reqbufs);
return -ENOMEM;
}
set_bit(STATE_BUFS_ALLOCATED, &ctx->capture_state);
diff --git a/drivers/media/platform/exynos5-is/fimc-is.h b/drivers/media/platform/exynos5-is/fimc-is.h
index 2d654f7e4ea..c25eb13ff8b 100644
--- a/drivers/media/platform/exynos5-is/fimc-is.h
+++ b/drivers/media/platform/exynos5-is/fimc-is.h
@@ -87,16 +87,19 @@ static inline void fimc_is_isp_wait_queue_add(struct fimc_is_isp *isp,
{
list_add_tail(&buf->list, &isp->wait_queue);
isp->wait_queue_cnt++;
+ buf->state = FIMC_IS_BUF_QUEUED;
}
static inline struct fimc_is_buf *fimc_is_isp_wait_queue_get(
struct fimc_is_isp *isp)
{
struct fimc_is_buf *buf;
- buf = list_entry(isp->wait_queue.next,
+ buf = list_first_entry_or_null(&isp->wait_queue,
struct fimc_is_buf, list);
- list_del(&buf->list);
- isp->wait_queue_cnt--;
+ if (buf) {
+ list_del(&buf->list);
+ isp->wait_queue_cnt--;
+ }
return buf;
}
@@ -105,16 +108,20 @@ static inline void fimc_is_isp_run_queue_add(struct fimc_is_isp *isp,
{
list_add_tail(&buf->list, &isp->run_queue);
isp->run_queue_cnt++;
+ buf->state = FIMC_IS_BUF_ACTIVE;
}
static inline struct fimc_is_buf *fimc_is_isp_run_queue_get(
struct fimc_is_isp *isp)
{
struct fimc_is_buf *buf;
- buf = list_entry(isp->run_queue.next,
+ buf = list_first_entry_or_null(&isp->run_queue,
struct fimc_is_buf, list);
- list_del(&buf->list);
- isp->run_queue_cnt--;
+ if (buf) {
+ list_del(&buf->list);
+ isp->run_queue_cnt--;
+ buf->state = FIMC_IS_BUF_DONE;
+ }
return buf;
}
@@ -124,16 +131,19 @@ static inline void fimc_is_scaler_wait_queue_add(struct fimc_is_scaler *scp,
{
list_add_tail(&buf->list, &scp->wait_queue);
scp->wait_queue_cnt++;
+ buf->state = FIMC_IS_BUF_QUEUED;
}
static inline struct fimc_is_buf *fimc_is_scaler_wait_queue_get(
struct fimc_is_scaler *scp)
{
struct fimc_is_buf *buf;
- buf = list_entry(scp->wait_queue.next,
+ buf = list_first_entry_or_null(&scp->wait_queue,
struct fimc_is_buf, list);
- list_del(&buf->list);
- scp->wait_queue_cnt--;
+ if (buf) {
+ list_del(&buf->list);
+ scp->wait_queue_cnt--;
+ }
return buf;
}
@@ -142,16 +152,20 @@ static inline void fimc_is_scaler_run_queue_add(struct fimc_is_scaler *scp,
{
list_add_tail(&buf->list, &scp->run_queue);
scp->run_queue_cnt++;
+ buf->state = FIMC_IS_BUF_ACTIVE;
}
static inline struct fimc_is_buf *fimc_is_scaler_run_queue_get(
struct fimc_is_scaler *scp)
{
struct fimc_is_buf *buf;
- buf = list_entry(scp->run_queue.next,
+ buf = list_first_entry_or_null(&scp->run_queue,
struct fimc_is_buf, list);
- list_del(&buf->list);
- scp->run_queue_cnt--;
+ if (buf) {
+ list_del(&buf->list);
+ scp->run_queue_cnt--;
+ buf->state = FIMC_IS_BUF_DONE;
+ }
return buf;
}