457 lines
12 KiB
C
457 lines
12 KiB
C
/* Copyright (c) 2012-2015, 2019 The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "CAM-VB2 %s:%d " fmt, __func__, __LINE__
|
|
#include "msm_vb2.h"
|
|
|
|
static int msm_vb2_queue_setup(struct vb2_queue *q,
|
|
const struct v4l2_format *fmt,
|
|
unsigned int *num_buffers, unsigned int *num_planes,
|
|
unsigned int sizes[], void *alloc_ctxs[])
|
|
{
|
|
int i;
|
|
struct msm_v4l2_format_data *data = NULL;
|
|
int rc = -EINVAL;
|
|
|
|
mutex_lock(q->lock);
|
|
data = q->drv_priv;
|
|
|
|
if (!data) {
|
|
pr_err("%s: drv_priv NULL\n", __func__);
|
|
goto done;
|
|
}
|
|
if (data->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
|
|
if (WARN_ON(data->num_planes > VIDEO_MAX_PLANES))
|
|
goto done;
|
|
|
|
*num_planes = data->num_planes;
|
|
|
|
for (i = 0; i < data->num_planes; i++)
|
|
sizes[i] = data->plane_sizes[i];
|
|
} else {
|
|
pr_err("%s: Unsupported buf type :%d\n", __func__,
|
|
data->type);
|
|
goto done;
|
|
}
|
|
rc = 0;
|
|
|
|
done:
|
|
mutex_unlock(q->lock);
|
|
return rc;
|
|
}
|
|
|
|
int msm_vb2_buf_init(struct vb2_buffer *vb)
|
|
{
|
|
struct msm_stream *stream;
|
|
struct msm_session *session;
|
|
struct msm_vb2_buffer *msm_vb2_buf;
|
|
unsigned long rl_flags;
|
|
|
|
session = msm_get_session_from_vb2q(vb->vb2_queue);
|
|
if (IS_ERR_OR_NULL(session))
|
|
return -EINVAL;
|
|
|
|
read_lock_irqsave(&session->stream_rwlock, rl_flags);
|
|
|
|
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
|
|
if (!stream) {
|
|
pr_err("%s: Couldn't find stream\n", __func__);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return -EINVAL;
|
|
}
|
|
msm_vb2_buf = container_of(vb, struct msm_vb2_buffer, vb2_buf);
|
|
msm_vb2_buf->in_freeq = 0;
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return 0;
|
|
}
|
|
|
|
static void msm_vb2_buf_queue(struct vb2_buffer *vb)
|
|
{
|
|
struct msm_vb2_buffer *msm_vb2;
|
|
struct msm_stream *stream;
|
|
struct msm_session *session;
|
|
unsigned long flags, rl_flags;
|
|
|
|
msm_vb2 = container_of(vb, struct msm_vb2_buffer, vb2_buf);
|
|
|
|
if (!msm_vb2) {
|
|
pr_err("%s:%d] vb2_buf NULL", __func__, __LINE__);
|
|
return;
|
|
}
|
|
|
|
session = msm_get_session_from_vb2q(vb->vb2_queue);
|
|
if (IS_ERR_OR_NULL(session))
|
|
return;
|
|
|
|
read_lock_irqsave(&session->stream_rwlock, rl_flags);
|
|
|
|
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
|
|
if (!stream) {
|
|
pr_err("%s:%d] NULL stream", __func__, __LINE__);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return;
|
|
}
|
|
|
|
spin_lock_irqsave(&stream->stream_lock, flags);
|
|
list_add_tail(&msm_vb2->list, &stream->queued_list);
|
|
spin_unlock_irqrestore(&stream->stream_lock, flags);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
}
|
|
|
|
static int msm_vb2_buf_finish(struct vb2_buffer *vb)
|
|
{
|
|
struct msm_vb2_buffer *msm_vb2;
|
|
struct msm_stream *stream;
|
|
struct msm_session *session;
|
|
unsigned long flags, rl_flags;
|
|
struct msm_vb2_buffer *msm_vb2_entry, *temp;
|
|
|
|
msm_vb2 = container_of(vb, struct msm_vb2_buffer, vb2_buf);
|
|
|
|
if (!msm_vb2) {
|
|
pr_err("%s:%d] vb2_buf NULL", __func__, __LINE__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
session = msm_get_session_from_vb2q(vb->vb2_queue);
|
|
if (IS_ERR_OR_NULL(session))
|
|
return -EINVAL;
|
|
|
|
read_lock_irqsave(&session->stream_rwlock, rl_flags);
|
|
|
|
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
|
|
if (!stream) {
|
|
pr_err("%s:%d] NULL stream", __func__, __LINE__);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return -EINVAL;
|
|
}
|
|
|
|
spin_lock_irqsave(&stream->stream_lock, flags);
|
|
list_for_each_entry_safe(msm_vb2_entry, temp, &(stream->queued_list),
|
|
list) {
|
|
if (msm_vb2_entry == msm_vb2) {
|
|
list_del_init(&msm_vb2_entry->list);
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&stream->stream_lock, flags);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return 0;
|
|
}
|
|
|
|
static void msm_vb2_buf_cleanup(struct vb2_buffer *vb)
|
|
{
|
|
struct msm_vb2_buffer *msm_vb2;
|
|
struct msm_stream *stream;
|
|
struct msm_session *session;
|
|
unsigned long flags, rl_flags;
|
|
|
|
msm_vb2 = container_of(vb, struct msm_vb2_buffer, vb2_buf);
|
|
|
|
if (!msm_vb2) {
|
|
pr_err("%s:%d] vb2 NULL", __func__, __LINE__);
|
|
return;
|
|
}
|
|
|
|
session = msm_get_session_from_vb2q(vb->vb2_queue);
|
|
if (IS_ERR_OR_NULL(session))
|
|
return;
|
|
|
|
read_lock_irqsave(&session->stream_rwlock, rl_flags);
|
|
|
|
stream = msm_get_stream_from_vb2q(vb->vb2_queue);
|
|
if (!stream) {
|
|
pr_err_ratelimited("%s:%d] NULL stream", __func__, __LINE__);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return;
|
|
}
|
|
|
|
spin_lock_irqsave(&stream->stream_lock, flags);
|
|
INIT_LIST_HEAD(&stream->queued_list);
|
|
spin_unlock_irqrestore(&stream->stream_lock, flags);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
}
|
|
|
|
int msm_vb2_get_stream_state(struct msm_stream *stream)
|
|
{
|
|
struct msm_vb2_buffer *msm_vb2, *temp;
|
|
unsigned long flags;
|
|
int rc = 1;
|
|
|
|
spin_lock_irqsave(&stream->stream_lock, flags);
|
|
list_for_each_entry_safe(msm_vb2, temp, &(stream->queued_list), list) {
|
|
if (msm_vb2->in_freeq != 0) {
|
|
rc = 0;
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&stream->stream_lock, flags);
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(msm_vb2_get_stream_state);
|
|
|
|
static struct vb2_ops msm_vb2_get_q_op = {
|
|
.queue_setup = msm_vb2_queue_setup,
|
|
.buf_init = msm_vb2_buf_init,
|
|
.buf_queue = msm_vb2_buf_queue,
|
|
.buf_cleanup = msm_vb2_buf_cleanup,
|
|
.buf_finish = msm_vb2_buf_finish,
|
|
};
|
|
|
|
|
|
struct vb2_ops *msm_vb2_get_q_ops(void)
|
|
{
|
|
return &msm_vb2_get_q_op;
|
|
}
|
|
|
|
static void *msm_vb2_dma_contig_get_userptr(void *alloc_ctx,
|
|
unsigned long vaddr, unsigned long size, int write)
|
|
{
|
|
struct msm_vb2_private_data *priv;
|
|
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
|
if (!priv)
|
|
return ERR_PTR(-ENOMEM);
|
|
priv->vaddr = (void *)vaddr;
|
|
priv->size = size;
|
|
priv->alloc_ctx = alloc_ctx;
|
|
return priv;
|
|
}
|
|
|
|
static void msm_vb2_dma_contig_put_userptr(void *buf_priv)
|
|
{
|
|
kzfree(buf_priv);
|
|
}
|
|
|
|
static struct vb2_mem_ops msm_vb2_get_q_mem_op = {
|
|
.get_userptr = msm_vb2_dma_contig_get_userptr,
|
|
.put_userptr = msm_vb2_dma_contig_put_userptr,
|
|
};
|
|
|
|
struct vb2_mem_ops *msm_vb2_get_q_mem_ops(void)
|
|
{
|
|
return &msm_vb2_get_q_mem_op;
|
|
}
|
|
|
|
static struct vb2_queue *msm_vb2_get_queue(int session_id,
|
|
unsigned int stream_id)
|
|
{
|
|
return msm_get_stream_vb2q(session_id, stream_id);
|
|
}
|
|
|
|
static struct vb2_buffer *msm_vb2_get_buf(int session_id,
|
|
unsigned int stream_id)
|
|
{
|
|
struct msm_stream *stream;
|
|
struct msm_session *session;
|
|
struct vb2_buffer *vb2_buf = NULL;
|
|
struct msm_vb2_buffer *msm_vb2 = NULL;
|
|
unsigned long flags, rl_flags;
|
|
|
|
session = msm_get_session(session_id);
|
|
if (IS_ERR_OR_NULL(session))
|
|
return NULL;
|
|
|
|
read_lock_irqsave(&session->stream_rwlock, rl_flags);
|
|
|
|
stream = msm_get_stream(session, stream_id);
|
|
if (IS_ERR_OR_NULL(stream)) {
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return NULL;
|
|
}
|
|
|
|
spin_lock_irqsave(&stream->stream_lock, flags);
|
|
|
|
if (!stream->vb2_q) {
|
|
pr_err("%s: stream q not available\n", __func__);
|
|
goto end;
|
|
}
|
|
|
|
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
|
|
vb2_buf = &(msm_vb2->vb2_buf);
|
|
if (vb2_buf->state != VB2_BUF_STATE_ACTIVE)
|
|
continue;
|
|
|
|
if (msm_vb2->in_freeq)
|
|
continue;
|
|
|
|
msm_vb2->in_freeq = 1;
|
|
goto end;
|
|
}
|
|
msm_vb2 = NULL;
|
|
vb2_buf = NULL;
|
|
end:
|
|
spin_unlock_irqrestore(&stream->stream_lock, flags);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return vb2_buf;
|
|
}
|
|
|
|
static int msm_vb2_put_buf(struct vb2_buffer *vb, int session_id,
|
|
unsigned int stream_id)
|
|
{
|
|
struct msm_stream *stream;
|
|
struct msm_session *session;
|
|
struct msm_vb2_buffer *msm_vb2;
|
|
struct vb2_buffer *vb2_buf = NULL;
|
|
int rc = 0;
|
|
unsigned long flags, rl_flags;
|
|
|
|
session = msm_get_session(session_id);
|
|
if (IS_ERR_OR_NULL(session))
|
|
return -EINVAL;
|
|
|
|
read_lock_irqsave(&session->stream_rwlock, rl_flags);
|
|
|
|
stream = msm_get_stream(session, stream_id);
|
|
if (IS_ERR_OR_NULL(stream)) {
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return -EINVAL;
|
|
}
|
|
|
|
spin_lock_irqsave(&stream->stream_lock, flags);
|
|
if (vb) {
|
|
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
|
|
vb2_buf = &(msm_vb2->vb2_buf);
|
|
if (vb2_buf == vb)
|
|
break;
|
|
}
|
|
if (vb2_buf != vb) {
|
|
pr_err("VB buffer is INVALID vb=%pK, ses_id=%d, str_id=%d\n",
|
|
vb, session_id, stream_id);
|
|
spin_unlock_irqrestore(&stream->stream_lock, flags);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return -EINVAL;
|
|
}
|
|
msm_vb2 =
|
|
container_of(vb, struct msm_vb2_buffer, vb2_buf);
|
|
if (msm_vb2->in_freeq) {
|
|
msm_vb2->in_freeq = 0;
|
|
rc = 0;
|
|
} else
|
|
rc = -EINVAL;
|
|
} else {
|
|
pr_err(" VB buffer is null for ses_id=%d, str_id=%d\n",
|
|
session_id, stream_id);
|
|
rc = -EINVAL;
|
|
}
|
|
spin_unlock_irqrestore(&stream->stream_lock, flags);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return rc;
|
|
}
|
|
|
|
static int msm_vb2_buf_done(struct vb2_buffer *vb, int session_id,
|
|
unsigned int stream_id, uint32_t sequence,
|
|
struct timeval *ts, uint32_t reserved)
|
|
{
|
|
unsigned long flags, rl_flags;
|
|
struct msm_vb2_buffer *msm_vb2;
|
|
struct msm_stream *stream;
|
|
struct msm_session *session;
|
|
struct vb2_buffer *vb2_buf = NULL;
|
|
int rc = 0;
|
|
|
|
session = msm_get_session(session_id);
|
|
if (IS_ERR_OR_NULL(session))
|
|
return 0;
|
|
|
|
read_lock_irqsave(&session->stream_rwlock, rl_flags);
|
|
|
|
stream = msm_get_stream(session, stream_id);
|
|
if (IS_ERR_OR_NULL(stream)) {
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return -EINVAL;
|
|
}
|
|
|
|
spin_lock_irqsave(&stream->stream_lock, flags);
|
|
if (vb) {
|
|
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
|
|
vb2_buf = &(msm_vb2->vb2_buf);
|
|
if (vb2_buf == vb)
|
|
break;
|
|
}
|
|
if (vb2_buf != vb) {
|
|
pr_err("VB buffer is INVALID ses_id=%d, str_id=%d, vb=%pK\n",
|
|
session_id, stream_id, vb);
|
|
spin_unlock_irqrestore(&stream->stream_lock, flags);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return -EINVAL;
|
|
}
|
|
msm_vb2 =
|
|
container_of(vb, struct msm_vb2_buffer, vb2_buf);
|
|
/* put buf before buf done */
|
|
if (msm_vb2->in_freeq) {
|
|
vb->v4l2_buf.sequence = sequence;
|
|
vb->v4l2_buf.timestamp = *ts;
|
|
vb->v4l2_buf.reserved = reserved;
|
|
vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
|
|
msm_vb2->in_freeq = 0;
|
|
rc = 0;
|
|
} else
|
|
rc = -EINVAL;
|
|
} else {
|
|
pr_err(" VB buffer is NULL for ses_id=%d, str_id=%d\n",
|
|
session_id, stream_id);
|
|
rc = -EINVAL;
|
|
}
|
|
spin_unlock_irqrestore(&stream->stream_lock, flags);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return rc;
|
|
}
|
|
|
|
static int msm_vb2_flush_buf(int session_id, unsigned int stream_id)
|
|
{
|
|
unsigned long flags, rl_flags;
|
|
struct msm_vb2_buffer *msm_vb2;
|
|
struct msm_session *session;
|
|
struct msm_stream *stream;
|
|
struct vb2_buffer *vb2_buf = NULL;
|
|
|
|
session = msm_get_session(session_id);
|
|
if (IS_ERR_OR_NULL(session))
|
|
return -EINVAL;
|
|
|
|
read_lock_irqsave(&session->stream_rwlock, rl_flags);
|
|
|
|
stream = msm_get_stream(session, stream_id);
|
|
if (IS_ERR_OR_NULL(stream)) {
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return -EINVAL;
|
|
}
|
|
|
|
spin_lock_irqsave(&stream->stream_lock, flags);
|
|
list_for_each_entry(msm_vb2, &(stream->queued_list), list) {
|
|
vb2_buf = &(msm_vb2->vb2_buf);
|
|
/* Do buf done for all buffers*/
|
|
vb2_buffer_done(vb2_buf, VB2_BUF_STATE_DONE);
|
|
msm_vb2->in_freeq = 0;
|
|
}
|
|
spin_unlock_irqrestore(&stream->stream_lock, flags);
|
|
read_unlock_irqrestore(&session->stream_rwlock, rl_flags);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int msm_vb2_request_cb(struct msm_sd_req_vb2_q *req)
|
|
{
|
|
if (!req) {
|
|
pr_err("%s: suddev is null\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
req->get_buf = msm_vb2_get_buf;
|
|
req->get_vb2_queue = msm_vb2_get_queue;
|
|
req->put_buf = msm_vb2_put_buf;
|
|
req->buf_done = msm_vb2_buf_done;
|
|
req->flush_buf = msm_vb2_flush_buf;
|
|
|
|
return 0;
|
|
}
|