1697 lines
39 KiB
C
Executable file
1697 lines
39 KiB
C
Executable file
/*
|
|
* Samsung Exynos SoC series VPU driver
|
|
*
|
|
* Copyright (c) 2015 Samsung Electronics Co., Ltd
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/version.h>
|
|
#include <linux/types.h>
|
|
#include <linux/time.h>
|
|
#include <linux/slab.h>
|
|
#include <media/videobuf2-dma-sg.h>
|
|
#include <linux/dma-buf.h>
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0)
|
|
#include <linux/ion.h>
|
|
#else
|
|
#include <linux/ion_exynos.h>
|
|
#endif
|
|
|
|
#include "vision-config.h"
|
|
#include "vision-buffer.h"
|
|
|
|
#include "../npu/core/npu-vertex.h"
|
|
|
|
#include <asm/cacheflush.h>
|
|
|
|
#define DEBUG_SENTENCE_MAX 300
|
|
#define TERM_SIZE 50
|
|
#define VISION_QBUF 0xFA36
|
|
#define VISION_DQBUF 0x36FA
|
|
|
|
/*****************************************************************************
|
|
***** wrapper function *****
|
|
*****************************************************************************/
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0)
|
|
static dma_addr_t vision_dma_buf_dva_map(struct vb_buffer *buffer,
|
|
__attribute__((unused))u32 size)
|
|
{
|
|
return sg_dma_address(buffer->sgt->sgl);
|
|
}
|
|
|
|
static void vision_dma_buf_dva_unmap(
|
|
__attribute__((unused))struct vb_buffer *buffer)
|
|
{
|
|
return;
|
|
}
|
|
/*
|
|
static void *vision_dma_buf_va_map(struct vb_buffer *buffer)
|
|
{
|
|
return dma_buf_kmap(buffer->dma_buf, 0);
|
|
}
|
|
|
|
static void vision_dma_buf_va_unmap(struct vb_buffer *buffer)
|
|
{
|
|
dma_buf_kunmap(buffer->dma_buf, 0, buffer->vaddr);
|
|
}
|
|
*/
|
|
static void vision_dma_buf_add_attr(struct dma_buf_attachment *attachment)
|
|
{
|
|
attachment->dma_map_attrs |= DMA_ATTR_SKIP_CPU_SYNC;
|
|
}
|
|
|
|
static void vision_dma_buf_sync(struct vb_buffer *buffers,
|
|
__attribute__((unused))u32 size,
|
|
u32 direction, u32 action)
|
|
{
|
|
unsigned long flags;
|
|
|
|
// With sync fence (temperary solution or POC)
|
|
// In KPI mode, we adapted 'direct path'
|
|
// which is message-getting process of execution in 'interrupt context'
|
|
// If we allow IRQ in dma_buf_sync, it can cause
|
|
// abnormal scheduling in interrupt context
|
|
// (FW response of 'sync' ioctl vs qbuf of next execution)
|
|
if (is_kpi_mode_enabled(true))
|
|
local_irq_save(flags);
|
|
|
|
if (action == VISION_QBUF)
|
|
dma_buf_end_cpu_access(buffers->dma_buf, direction);
|
|
else // action == VISION_DQBUF
|
|
dma_buf_begin_cpu_access(buffers->dma_buf, direction);
|
|
|
|
if (is_kpi_mode_enabled(true))
|
|
local_irq_restore(flags);
|
|
}
|
|
#else
|
|
static dma_addr_t vision_dma_buf_dva_map(struct vb_buffer *buffer, u32 size)
|
|
{
|
|
return ion_iovmm_map(buffer->attachment, 0, size,
|
|
DMA_BIDIRECTIONAL, 0);
|
|
}
|
|
|
|
static void vision_dma_buf_dva_unmap(struct vb_buffer *buffer)
|
|
{
|
|
ion_iovmm_unmap(buffer->attachment, buffer->daddr);
|
|
}
|
|
/*
|
|
static void *vision_dma_buf_va_map(struct vb_buffer *buffer)
|
|
{
|
|
return dma_buf_vmap(buffer->dma_buf);
|
|
}
|
|
|
|
static void vision_dma_buf_va_unmap(struct vb_buffer *buffer)
|
|
{
|
|
dma_buf_vunmap(buffer->dma_buf, buffer->vaddr);
|
|
}
|
|
*/
|
|
|
|
static void vision_dma_buf_add_attr(struct dma_buf_attachment *attachment)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static void vision_dma_buf_sync(struct vb_buffer *buffers,
|
|
u32 size, u32 direction,
|
|
__attribute__((unused))u32 action)
|
|
{
|
|
unsigned long flags;
|
|
|
|
if (is_kpi_mode_enabled(true))
|
|
local_irq_save(flags);
|
|
|
|
__dma_map_area(buffers->vaddr, size, direction);
|
|
|
|
if (is_kpi_mode_enabled(true))
|
|
local_irq_restore(flags);
|
|
}
|
|
|
|
#endif
|
|
|
|
struct vision_debug_log {
|
|
size_t dsentence_pos;
|
|
char *dsentence/*[DEBUG_SENTENCE_MAX]*/;
|
|
};
|
|
|
|
void vision_dmsg_concate(struct vision_debug_log *log, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char *term/*[50]*/;
|
|
size_t copy_len;
|
|
|
|
term = kmalloc(TERM_SIZE, GFP_KERNEL);
|
|
va_start(ap, fmt);
|
|
vsnprintf(term, TERM_SIZE/*sizeof(term)*/, fmt, ap);
|
|
va_end(ap);
|
|
|
|
if (log->dsentence_pos >= DEBUG_SENTENCE_MAX) {
|
|
vision_err("debug message(%zd) over max\n", log->dsentence_pos);
|
|
kfree(term);
|
|
return;
|
|
}
|
|
|
|
copy_len = min((DEBUG_SENTENCE_MAX-log->dsentence_pos-1), strlen(term));
|
|
strncpy(log->dsentence + log->dsentence_pos, term, copy_len);
|
|
log->dsentence_pos += copy_len;
|
|
log->dsentence[log->dsentence_pos] = 0;
|
|
kfree(term);
|
|
}
|
|
|
|
char *vision_dmsg_print(struct vision_debug_log *log)
|
|
{
|
|
log->dsentence_pos = 0;
|
|
return log->dsentence;
|
|
}
|
|
|
|
#define DLOG_INIT() \
|
|
struct vision_debug_log vision_debug_log; \
|
|
vision_debug_log.dsentence_pos = 0; \
|
|
vision_debug_log.dsentence = kmalloc(DEBUG_SENTENCE_MAX, GFP_KERNEL);
|
|
#define DLOG(fmt, ...) \
|
|
vision_dmsg_concate(&vision_debug_log, fmt, ##__VA_ARGS__)
|
|
#define DLOG_OUT() vision_dmsg_print(&vision_debug_log)
|
|
#define DLOG_DEINIT() \
|
|
kfree(vision_debug_log.dsentence)
|
|
|
|
struct vb_fmt vb_fmts[] = {
|
|
{
|
|
.name = "RGB",
|
|
.colorspace = VS4L_DF_IMAGE_RGB,
|
|
.planes = 1,
|
|
.bitsperpixel = { 24 }
|
|
}, {
|
|
.name = "ARGB",
|
|
.colorspace = VS4L_DF_IMAGE_RGBX,
|
|
.planes = 1,
|
|
.bitsperpixel = { 32 }
|
|
}, {
|
|
.name = "YUV 4:2:0 planar, Y/CbCr",
|
|
.colorspace = VS4L_DF_IMAGE_NV12,
|
|
.planes = 2,
|
|
.bitsperpixel = { 8, 8 }
|
|
}, {
|
|
.name = "YUV 4:2:0 planar, Y/CrCb",
|
|
.colorspace = VS4L_DF_IMAGE_NV21,
|
|
.planes = 2,
|
|
.bitsperpixel = { 8, 8 }
|
|
}, {
|
|
.name = "YUV 4:2:2 packed, YCbYCr",
|
|
.colorspace = VS4L_DF_IMAGE_YUYV,
|
|
.planes = 1,
|
|
.bitsperpixel = { 16 }
|
|
}, {
|
|
.name = "YUV 4:4:4 packed, YCbCr",
|
|
.colorspace = VS4L_DF_IMAGE_YUV4,
|
|
.planes = 1,
|
|
.bitsperpixel = { 24 }
|
|
}, {
|
|
.name = "VX unsigned 8 bit",
|
|
.colorspace = VS4L_DF_IMAGE_U8,
|
|
.planes = 1,
|
|
.bitsperpixel = { 8 }
|
|
}, {
|
|
.name = "VX unsigned 16 bit",
|
|
.colorspace = VS4L_DF_IMAGE_U16,
|
|
.planes = 1,
|
|
.bitsperpixel = { 16 }
|
|
}, {
|
|
.name = "VX unsigned 32 bit",
|
|
.colorspace = VS4L_DF_IMAGE_U32,
|
|
.planes = 1,
|
|
.bitsperpixel = { 32 }
|
|
}, {
|
|
.name = "VX signed 16 bit",
|
|
.colorspace = VS4L_DF_IMAGE_S16,
|
|
.planes = 1,
|
|
.bitsperpixel = { 16 }
|
|
}, {
|
|
.name = "VX signed 32 bit",
|
|
.colorspace = VS4L_DF_IMAGE_S32,
|
|
.planes = 1,
|
|
.bitsperpixel = { 32 }
|
|
}, {
|
|
.name = "NPU FORMAT",
|
|
.colorspace = VS4L_DF_IMAGE_NPU,
|
|
.planes = 1,
|
|
.bitsperpixel = { 8 }
|
|
}, {
|
|
.name = "DSP FORMAT",
|
|
.colorspace = VS4L_DF_IMAGE_DSP,
|
|
.planes = 1,
|
|
.bitsperpixel = { 8 }
|
|
}
|
|
};
|
|
|
|
void __vb_queue_print(struct vb_queue *q)
|
|
{
|
|
struct vb_bundle *bundle, *temp;
|
|
DLOG_INIT();
|
|
|
|
DLOG("[VB] queued(%d) :", atomic_read(&q->queued_count));
|
|
list_for_each_entry_safe(bundle, temp, &q->queued_list, queued_entry) {
|
|
DLOG("%d->", bundle->clist.index);
|
|
}
|
|
DLOG("X");
|
|
|
|
vision_info("%s\n", DLOG_OUT());
|
|
|
|
DLOG("[VB] process(%d) :", atomic_read(&q->process_count));
|
|
list_for_each_entry_safe(
|
|
bundle, temp, &q->process_list, process_entry) {
|
|
DLOG("%d->", bundle->clist.index);
|
|
}
|
|
DLOG("X");
|
|
|
|
vision_info("%s\n", DLOG_OUT());
|
|
|
|
DLOG("[VB] done(%d) :", atomic_read(&q->done_count));
|
|
list_for_each_entry_safe(bundle, temp, &q->done_list, done_entry) {
|
|
DLOG("%d->", bundle->clist.index);
|
|
}
|
|
DLOG("X");
|
|
|
|
vision_info("%s\n", DLOG_OUT());
|
|
DLOG_DEINIT();
|
|
}
|
|
|
|
void __vb_buffer_print(struct vs4l_container_list *c)
|
|
{
|
|
vision_info("[VB] c->direction : %d", c->direction);
|
|
vision_info("[VB] c->id : %d", c->id);
|
|
vision_info("[VB] c->index : %d", c->count);
|
|
}
|
|
|
|
static struct vb_fmt *__vb_find_format(u32 colorspace)
|
|
{
|
|
size_t i;
|
|
struct vb_fmt *fmt = NULL;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(vb_fmts); ++i) {
|
|
if (vb_fmts[i].colorspace == colorspace) {
|
|
fmt = &vb_fmts[i];
|
|
break;
|
|
}
|
|
}
|
|
return fmt;
|
|
}
|
|
|
|
static int __vb_plane_size(struct vb_format *format)
|
|
{
|
|
int ret = 0;
|
|
u32 plane;
|
|
struct vb_fmt *fmt;
|
|
|
|
BUG_ON(!format);
|
|
fmt = format->fmt;
|
|
if (fmt->planes > VB_MAX_PLANES) {
|
|
vision_err("planes(%d) is invalid\n", fmt->planes);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
for (plane = 0; plane < fmt->planes; ++plane) {
|
|
if (fmt->colorspace == VS4L_DF_IMAGE_NPU ||
|
|
fmt->colorspace == VS4L_DF_IMAGE_DSP) {
|
|
format->size[plane] = (format->pixel_format / 8) *
|
|
format->channels * format->width * format->height;
|
|
|
|
vision_info("plane[%u] bpp : %u, ch : %u, w : %u, h : %u, size : %u\n",
|
|
plane, format->pixel_format, format->channels,
|
|
format->width, format->height, format->size[plane]);
|
|
} else {
|
|
format->size[plane] = (fmt->bitsperpixel[plane] / 8) *
|
|
format->width * format->height;
|
|
|
|
vision_info("plane[%u] bpp : %u, ch : %u, w : %u, h : %u, size : %u\n",
|
|
plane, fmt->bitsperpixel[plane], format->channels,
|
|
format->width, format->height, format->size[plane]);
|
|
}
|
|
}
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
#if 1 //IS_ENABLED(CONFIG_ION_EXYNOS) || defined(CONFIG_ION_EXYNOS)
|
|
static int __vb_unmap_dmabuf(struct vb_queue *q, struct vb_buffer *buffer)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (buffer == NULL) {
|
|
vision_err("vb_buffer(buffer) is NULL\n");
|
|
ret = -EFAULT;
|
|
goto p_err;
|
|
}
|
|
|
|
if (buffer->vaddr)
|
|
dma_buf_vunmap(buffer->dma_buf, buffer->vaddr);
|
|
if (buffer->daddr)
|
|
vision_dma_buf_dva_unmap(buffer);
|
|
if (buffer->sgt)
|
|
dma_buf_unmap_attachment(
|
|
buffer->attachment, buffer->sgt, DMA_BIDIRECTIONAL);
|
|
if (buffer->attachment)
|
|
dma_buf_detach(buffer->dma_buf, buffer->attachment);
|
|
if (buffer->dma_buf)
|
|
dma_buf_put(buffer->dma_buf);
|
|
|
|
buffer->attachment = NULL;
|
|
buffer->sgt = NULL;
|
|
buffer->daddr = 0;
|
|
buffer->vaddr = NULL;
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if 0 // !(IS_ENABLED(CONFIG_ION_EXYNOS)) && !(defined(CONFIG_ION_EXYNOS))
|
|
static int __vb_unmap_virtptr(struct vb_queue *q, struct vb_buffer *buffer)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (buffer == NULL) {
|
|
vision_err("vb_buffer(buffer) is NULL\n");
|
|
ret = -EFAULT;
|
|
goto p_err;
|
|
}
|
|
|
|
if (buffer->reserved)
|
|
kfree((void *)buffer->m.userptr);
|
|
|
|
buffer->dma_buf = NULL;
|
|
buffer->attachment = NULL;
|
|
buffer->sgt = NULL;
|
|
buffer->daddr = 0;
|
|
buffer->vaddr = NULL;
|
|
buffer->reserved = 0;
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if 1 //IS_ENABLED(CONFIG_ION_EXYNOS) || defined(CONFIG_ION_EXYNOS)
|
|
static int __vb_map_dmabuf(
|
|
struct vb_queue *q, struct vb_buffer *buffer, u32 size)
|
|
{
|
|
int ret = 0;
|
|
bool complete_suc = false;
|
|
|
|
struct dma_buf *dma_buf;
|
|
struct dma_buf_attachment *attachment;
|
|
struct sg_table *sgt;
|
|
dma_addr_t daddr;
|
|
void *vaddr;
|
|
u32 direction;
|
|
|
|
buffer->dma_buf = NULL;
|
|
buffer->attachment = NULL;
|
|
buffer->sgt = NULL;
|
|
buffer->daddr = 0;
|
|
buffer->vaddr = NULL;
|
|
|
|
dma_buf = dma_buf_get(buffer->m.fd);
|
|
if (IS_ERR_OR_NULL(dma_buf)) {
|
|
vision_err("dma_buf_get is fail(%p)\n", dma_buf);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
buffer->dma_buf = dma_buf;
|
|
|
|
if (buffer->dma_buf->size < size) {
|
|
vision_err("Allocate buffer size(%zu) is smaller than expectation(%u)\n",
|
|
buffer->dma_buf->size, size);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
attachment = dma_buf_attach(buffer->dma_buf, q->alloc_ctx);
|
|
if (IS_ERR(attachment)) {
|
|
ret = PTR_ERR(attachment);
|
|
goto p_err;
|
|
}
|
|
buffer->attachment = attachment;
|
|
|
|
vision_dma_buf_add_attr(attachment);
|
|
|
|
if (q->direction == VS4L_DIRECTION_OT)
|
|
direction = DMA_FROM_DEVICE;
|
|
else
|
|
direction = DMA_TO_DEVICE;
|
|
|
|
sgt = dma_buf_map_attachment(attachment, direction);
|
|
if (IS_ERR(sgt)) {
|
|
ret = PTR_ERR(sgt);
|
|
goto p_err;
|
|
}
|
|
buffer->sgt = sgt;
|
|
|
|
daddr = vision_dma_buf_dva_map(buffer, size);
|
|
if (IS_ERR_VALUE(daddr)) {
|
|
vision_err("Failed to allocate iova (err 0x%pK)\n", &daddr);
|
|
ret = -ENOMEM;
|
|
goto p_err;
|
|
}
|
|
buffer->daddr = daddr;
|
|
|
|
vaddr = dma_buf_vmap(buffer->dma_buf);
|
|
if (IS_ERR(vaddr)) {
|
|
vision_err("Failed to get vaddr (err 0x%pK)\n", &vaddr);
|
|
ret = -EFAULT;
|
|
goto p_err;
|
|
}
|
|
buffer->vaddr = vaddr;
|
|
|
|
complete_suc = true;
|
|
|
|
//vision_info("__vb_map_dmabuf, size(%d), daddr(0x%x), vaddr(0x%pK)\n",
|
|
//size, daddr, vaddr);
|
|
p_err:
|
|
if (complete_suc != true)
|
|
__vb_unmap_dmabuf(q, buffer);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if 0 //!(IS_ENABLED(CONFIG_ION_EXYNOS)) && !(defined(CONFIG_ION_EXYNOS))
|
|
static int __vb_map_virtptr(
|
|
struct vb_queue *q, struct vb_buffer *buffer, u32 size)
|
|
{
|
|
int ret = 0;
|
|
unsigned long tmp_buffer;
|
|
void *mbuf = NULL;
|
|
|
|
mbuf = kmalloc(size, GFP_KERNEL);
|
|
if (!mbuf) {
|
|
ret = -ENOMEM;
|
|
goto p_err;
|
|
}
|
|
|
|
tmp_buffer = (unsigned long)mbuf;
|
|
|
|
ret = copy_from_user((void *)tmp_buffer,
|
|
(void *)buffer->m.userptr, size);
|
|
if (ret) {
|
|
vision_err("copy_from_user() is fail(%d)\n", ret);
|
|
goto p_err;
|
|
}
|
|
|
|
/* back up - userptr */
|
|
buffer->reserved = buffer->m.userptr;
|
|
buffer->m.userptr = (unsigned long)tmp_buffer;
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int __vb_queue_alloc(struct vb_queue *q,
|
|
struct vs4l_container_list *c)
|
|
{
|
|
int ret = 0;
|
|
u32 i, j;
|
|
size_t alloc_size;
|
|
u8 *mapped_ptr;
|
|
struct vb_format_list *flist;
|
|
struct vb_bundle *bundle;
|
|
struct vb_container_list *clist;
|
|
struct vb_container *container;
|
|
struct vb_buffer *buffer;
|
|
DLOG_INIT();
|
|
|
|
BUG_ON(!q);
|
|
BUG_ON(!c);
|
|
BUG_ON(c->index >= VB_MAX_BUFFER);
|
|
|
|
flist = &q->format;
|
|
|
|
/* allocation */
|
|
if (c->count > VB_MAX_CONTAINERLIST) {
|
|
vision_err("c->count(%d) cannot be greater to VB_MAX_CONTAINERLIST(%d)\n", c->count, VB_MAX_CONTAINERLIST);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
alloc_size = sizeof(struct vb_bundle);
|
|
alloc_size += sizeof(struct vb_container) * c->count;
|
|
for (i = 0; i < c->count; ++i)
|
|
alloc_size += sizeof(struct vb_buffer) * c->containers[i].count;
|
|
|
|
bundle = kzalloc(alloc_size, GFP_KERNEL);
|
|
if (!bundle) {
|
|
vision_err("bundle is NULL\n");
|
|
ret = -ENOMEM;
|
|
goto p_err;
|
|
}
|
|
|
|
/* mapping */
|
|
mapped_ptr = (u8 *)bundle + sizeof(struct vb_bundle);
|
|
bundle->clist.containers = (struct vb_container *)mapped_ptr;
|
|
mapped_ptr += sizeof(struct vb_container) * c->count;
|
|
for (i = 0; i < c->count; ++i) {
|
|
bundle->clist.containers[i].buffers =
|
|
(struct vb_buffer *)mapped_ptr;
|
|
mapped_ptr += sizeof(struct vb_buffer) * c->containers[i].count;
|
|
}
|
|
|
|
/* fill */
|
|
bundle->state = VB_BUF_STATE_DEQUEUED;
|
|
clear_bit(VS4L_CL_FLAG_PREPARE, &bundle->flags);
|
|
clear_bit(VS4L_CL_FLAG_INVALID, &bundle->flags);
|
|
clear_bit(VS4L_CL_FLAG_DONE, &bundle->flags);
|
|
clear_bit(VS4L_CL_FLAG_FW_TIMEOUT, &bundle->flags);
|
|
clear_bit(VS4L_CL_FLAG_HW_TIMEOUT_RECOVERED, &bundle->flags);
|
|
clear_bit(VS4L_CL_FLAG_HW_TIMEOUT_NOTRECOVERABLE, &bundle->flags);
|
|
|
|
clist = &bundle->clist;
|
|
clist->index = c->index;
|
|
clist->id = c->id;
|
|
clist->direction = c->direction;
|
|
clist->count = c->count;
|
|
clist->flags = c->flags;
|
|
if (c->timestamp[5].tv_usec)
|
|
clist->timestamp[5].tv_usec = c->timestamp[5].tv_usec;
|
|
|
|
for (i = 0; i < clist->count; ++i) {
|
|
container = &clist->containers[i];
|
|
container->count = c->containers[i].count;
|
|
container->memory = c->containers[i].memory;
|
|
container->reserved[0] = c->containers[i].reserved[0];
|
|
container->reserved[1] = c->containers[i].reserved[1];
|
|
container->reserved[2] = c->containers[i].reserved[2];
|
|
container->reserved[3] = c->containers[i].reserved[3];
|
|
container->target = c->containers[i].target;
|
|
container->type = c->containers[i].type;
|
|
|
|
for (j = 0; j < flist->count; ++j) {
|
|
DLOG("c%d t%d == f%d t%d\n", i, container->target,
|
|
j, flist->formats[j].target);
|
|
if (container->target == flist->formats[j].target) {
|
|
container->format = &flist->formats[j];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!container->format) {
|
|
vision_err("format is not found\n");
|
|
vision_err("%s\n", DLOG_OUT());
|
|
kfree(bundle);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
for (j = 0; j < container->count; ++j) {
|
|
buffer = &container->buffers[j];
|
|
buffer->roi = c->containers[i].buffers[j].roi;
|
|
buffer->m.userptr =
|
|
c->containers[i].buffers[j].m.userptr;
|
|
buffer->dma_buf = NULL;
|
|
buffer->attachment = NULL;
|
|
buffer->sgt = NULL;
|
|
buffer->vaddr = NULL;
|
|
buffer->daddr = 0;
|
|
}
|
|
}
|
|
|
|
q->bufs[c->index] = bundle;
|
|
q->num_buffers++;
|
|
|
|
p_err:
|
|
DLOG_DEINIT();
|
|
return ret;
|
|
}
|
|
|
|
static int __vb_queue_free(struct vb_queue *q,
|
|
struct vb_bundle *bundle)
|
|
{
|
|
int ret = 0;
|
|
|
|
BUG_ON(!bundle);
|
|
BUG_ON(bundle->clist.index >= VB_MAX_BUFFER);
|
|
|
|
if (q == NULL) {
|
|
vision_err("vb_queue(q) is NULL\n");
|
|
ret = -EFAULT;
|
|
goto p_err;
|
|
}
|
|
|
|
q->bufs[bundle->clist.index] = NULL;
|
|
kfree(bundle);
|
|
q->num_buffers--;
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static int __vb_queue_check(struct vb_bundle *bundle,
|
|
struct vs4l_container_list *c)
|
|
{
|
|
int ret = 0;
|
|
u32 i, j;
|
|
struct vb_container_list *clist;
|
|
struct vb_container *container;
|
|
struct vb_buffer *buffer;
|
|
|
|
BUG_ON(!bundle);
|
|
BUG_ON(!c);
|
|
|
|
clist = &bundle->clist;
|
|
|
|
if (clist->index != c->index) {
|
|
vision_err("index is conflict(%d != %d)\n",
|
|
clist->index, c->index);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
if (clist->direction != c->direction) {
|
|
vision_err("direction is conflict(%d != %d)\n",
|
|
clist->direction, c->direction);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
if (clist->count != c->count) {
|
|
vision_err("count is conflict(%d != %d)\n",
|
|
clist->count, c->count);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
clist->flags = c->flags;
|
|
clist->id = c->id;
|
|
if (c->timestamp[5].tv_usec)
|
|
clist->timestamp[5].tv_usec = c->timestamp[5].tv_usec;
|
|
|
|
for (i = 0; i < clist->count; ++i) {
|
|
container = &clist->containers[i];
|
|
|
|
if (container->target != c->containers[i].target) {
|
|
vision_err("target is conflict(%d != %d)\n",
|
|
container->target, c->containers[i].target);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
if (container->count != c->containers[i].count) {
|
|
vision_err("count is conflict(%d != %d)\n",
|
|
container->count, c->containers[i].count);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
for (j = 0; j < container->count; ++j) {
|
|
buffer = &container->buffers[j];
|
|
buffer->roi = c->containers[i].buffers[j].roi;
|
|
if (buffer->m.fd != c->containers[i].buffers[j].m.fd) {
|
|
vision_err("buffer is conflict(%d != %d)\n",
|
|
buffer->m.fd, c->containers[i].buffers[j].m.fd);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
}
|
|
}
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static int __vb_buf_prepare(struct vb_queue *q, struct vb_bundle *bundle)
|
|
{
|
|
int ret = 0;
|
|
u32 i = 0, j = 0, k = 0;
|
|
struct vb_format *format;
|
|
struct vb_container *container;
|
|
struct vb_buffer *buffer;
|
|
|
|
BUG_ON(!q);
|
|
BUG_ON(!bundle);
|
|
|
|
if (test_bit(VS4L_CL_FLAG_PREPARE, &bundle->flags))
|
|
return ret;
|
|
|
|
for (i = 0; i < bundle->clist.count; ++i) {
|
|
container = &bundle->clist.containers[i];
|
|
format = container->format;
|
|
|
|
switch (container->type) {
|
|
case VS4L_BUFFER_LIST:
|
|
k = container->count;
|
|
break;
|
|
case VS4L_BUFFER_ROI:
|
|
k = container->count;
|
|
break;
|
|
case VS4L_BUFFER_PYRAMID:
|
|
k = container->count;
|
|
break;
|
|
default:
|
|
vision_err("unsupported container type\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
switch (container->memory) {
|
|
#if 1 //IS_ENABLED(CONFIG_ION_EXYNOS) || defined(CONFIG_ION_EXYNOS)
|
|
case VS4L_MEMORY_DMABUF:
|
|
for (j = 0; j < k; ++j) {
|
|
buffer = &container->buffers[j];
|
|
if (format->colorspace == VS4L_DF_IMAGE_NPU ||
|
|
format->colorspace == VS4L_DF_IMAGE_DSP) {
|
|
ret = __vb_map_dmabuf(
|
|
q, buffer, format->size[0]);
|
|
vision_info("size : %u\n", format->size[0]);
|
|
} else {
|
|
ret = __vb_map_dmabuf(
|
|
q, buffer, format->size[format->plane]);
|
|
vision_info("size : %u\n", format->size[format->plane]);
|
|
}
|
|
if (ret) {
|
|
vision_err("__vb_qbuf_dmabuf is fail(%d)\n",
|
|
ret);
|
|
goto p_err;
|
|
}
|
|
vision_info("prepare dmabuf (%d) - clist : %d container : %d, fd : %d\n", __LINE__, i, j, buffer->m.fd);
|
|
}
|
|
break;
|
|
#else
|
|
case VS4L_MEMORY_VIRTPTR:
|
|
for (j = 0; j < k; ++j) {
|
|
buffer = &container->buffers[j];
|
|
|
|
if (format->colorspace == VS4L_DF_IMAGE_NPU ||
|
|
format->colorspace == VS4L_DF_IMAGE_DSP) {
|
|
ret = __vb_map_virtptr(
|
|
q, buffer, format->size[0]);
|
|
} else {
|
|
ret = __vb_map_virtptr(
|
|
q, buffer, format->size[format->plane]);
|
|
}
|
|
|
|
if (ret) {
|
|
vision_err("__vb_map_virtptr is fail(%d)\n",
|
|
ret);
|
|
goto p_err;
|
|
}
|
|
vision_info("prepare virtptr (%d) - clist : %d container : %d, fd : %d\n", __LINE__, i, j, buffer->m.fd);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
buffer = &container->buffers[j];
|
|
vision_err("unsupported container memory type i %d, memory %d, fd %d\n",
|
|
i, container->memory, buffer->m.fd);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
}
|
|
|
|
ret = call_op(q, buf_prepare, q, &bundle->clist);
|
|
if (ret) {
|
|
vision_err("call_op(buf_prepare) is fail(%d)\n", ret);
|
|
goto p_err;
|
|
}
|
|
|
|
set_bit(VS4L_CL_FLAG_PREPARE, &bundle->flags);
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static int __vb_buf_unprepare(struct vb_queue *q, struct vb_bundle *bundle)
|
|
{
|
|
int ret = 0;
|
|
u32 i, j, k;
|
|
struct vb_format *format;
|
|
struct vb_container *container;
|
|
struct vb_buffer *buffer;
|
|
|
|
BUG_ON(!q);
|
|
BUG_ON(!bundle);
|
|
|
|
if (!test_bit(VS4L_CL_FLAG_PREPARE, &bundle->flags))
|
|
return ret;
|
|
|
|
for (i = 0; i < bundle->clist.count; ++i) {
|
|
container = &bundle->clist.containers[i];
|
|
format = container->format;
|
|
|
|
switch (container->type) {
|
|
case VS4L_BUFFER_LIST:
|
|
k = container->count;
|
|
break;
|
|
case VS4L_BUFFER_ROI:
|
|
k = container->count;
|
|
break;
|
|
case VS4L_BUFFER_PYRAMID:
|
|
k = container->count;
|
|
break;
|
|
default:
|
|
vision_err("unsupported container type\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
switch (container->memory) {
|
|
#if 1 //IS_ENABLED(CONFIG_ION_EXYNOS) || defined(CONFIG_ION_EXYNOS)
|
|
case VS4L_MEMORY_DMABUF:
|
|
for (j = 0; j < k; ++j) {
|
|
buffer = &container->buffers[j];
|
|
ret = __vb_unmap_dmabuf(q, buffer);
|
|
if (ret) {
|
|
vision_err("__vb_qbuf_dmabuf is fail(%d)\n",
|
|
ret);
|
|
goto p_err;
|
|
}
|
|
vision_info("unprepare dmabuf (%d) - clist : %d container : %d, fd : %d\n", __LINE__, i, j, buffer->m.fd);
|
|
}
|
|
break;
|
|
#else
|
|
case VS4L_MEMORY_VIRTPTR:
|
|
for (j = 0; j < k; ++j) {
|
|
buffer = &container->buffers[j];
|
|
ret = __vb_unmap_virtptr(q, buffer);
|
|
if (ret) {
|
|
vision_err("__vb_unmap_virtptr is fail(%d)\n",
|
|
ret);
|
|
goto p_err;
|
|
}
|
|
vision_info("unprepare virtptr (%d)- clist : %d container : %d, fd : %d\n", __LINE__, i, j, buffer->m.fd);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
vision_err("unsupported container memory type\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
}
|
|
|
|
ret = call_op(q, buf_unprepare, q, &bundle->clist);
|
|
if (ret) {
|
|
vision_err("call_op(buf_unprepare) is fail(%d)\n", ret);
|
|
goto p_err;
|
|
}
|
|
|
|
clear_bit(VS4L_CL_FLAG_PREPARE, &bundle->flags);
|
|
|
|
return ret;
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
#ifdef CONFIG_NPU_USE_ASYNC
|
|
static bool __is_done_for_me(struct vb_queue *q,
|
|
struct vb_bundle **bundle,
|
|
struct vs4l_container_list *c)
|
|
{
|
|
unsigned long flags;
|
|
struct vb_bundle *b, *t;
|
|
bool ok = false;
|
|
|
|
if (likely(list_empty(&q->done_list)))
|
|
return false;
|
|
|
|
spin_lock_irqsave(&q->done_lock, flags);
|
|
list_for_each_entry_safe(b, t, &q->done_list, done_entry) {
|
|
if (b->clist.index == c->index) {
|
|
ok = true;
|
|
break;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&q->done_lock, flags);
|
|
|
|
if (ok)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
#endif
|
|
static int __vb_wait_for_done_vb(struct vb_queue *q,
|
|
struct vb_bundle **bundle,
|
|
struct vs4l_container_list *c,
|
|
int nonblocking)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!q->streaming) {
|
|
vision_err("Streaming off, will not wait for buffers\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
if (nonblocking) {
|
|
if (!list_empty(&q->done_list))
|
|
return ret;
|
|
|
|
vision_info("Nonblocking and no buffers to dequeue, will not wait\n");
|
|
ret = -EWOULDBLOCK;
|
|
goto p_err;
|
|
}
|
|
|
|
mutex_unlock(q->lock);
|
|
#ifdef CONFIG_NPU_USE_ASYNC
|
|
wait_event(q->done_wq, __is_done_for_me(q, bundle, c));
|
|
#else
|
|
wait_event(q->done_wq, !list_empty(&q->done_list));
|
|
#endif
|
|
mutex_lock(q->lock);
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static int __vb_get_done_vb(struct vb_queue *q,
|
|
struct vb_bundle **bundle,
|
|
struct vs4l_container_list *c,
|
|
int nonblocking)
|
|
{
|
|
unsigned long flags;
|
|
int ret = 0;
|
|
#ifdef CONFIG_NPU_USE_ASYNC
|
|
struct vb_bundle *b, *t;
|
|
#endif
|
|
|
|
/*
|
|
* Wait for at least one buffer to become available on the done_list.
|
|
*/
|
|
ret = __vb_wait_for_done_vb(q, bundle, c, nonblocking);
|
|
if (ret) {
|
|
vision_err("__vb_wait_for_done_vb is fail\n");
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Driver's lock has been held since we last verified that done_list
|
|
* is not empty, so no need for another list_empty(done_list) check.
|
|
*/
|
|
|
|
spin_lock_irqsave(&q->done_lock, flags);
|
|
#ifdef CONFIG_NPU_USE_ASYNC
|
|
list_for_each_entry_safe(b, t, &q->done_list, done_entry) {
|
|
if (b->clist.index == c->index) {
|
|
*bundle = b;
|
|
list_del(&(*bundle)->done_entry);
|
|
atomic_dec(&q->done_count);
|
|
vision_dbg("dqbuf bundle %p, id %d, index %d, count %d, dva %llx\n",
|
|
*bundle, b->clist.id, b->clist.index,
|
|
b->clist.count, b->clist.containers->buffers->daddr);
|
|
break;
|
|
}
|
|
}
|
|
#else
|
|
*bundle = list_first_entry(&q->done_list, struct vb_bundle, done_entry);
|
|
|
|
if (*bundle) {
|
|
list_del(&(*bundle)->done_entry);
|
|
atomic_dec(&q->done_count);
|
|
}
|
|
#endif
|
|
spin_unlock_irqrestore(&q->done_lock, flags);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __fill_vs4l_buffer(struct vb_bundle *bundle,
|
|
struct vs4l_container_list *c)
|
|
{
|
|
struct vb_container_list *clist;
|
|
|
|
clist = &bundle->clist;
|
|
c->flags &= ~(1 << VS4L_CL_FLAG_TIMESTAMP);
|
|
c->flags &= ~(1 << VS4L_CL_FLAG_PREPARE);
|
|
c->flags &= ~(1 << VS4L_CL_FLAG_INVALID);
|
|
c->flags &= ~(1 << VS4L_CL_FLAG_DONE);
|
|
c->flags &= ~(1 << VS4L_CL_FLAG_FW_TIMEOUT);
|
|
c->flags &= ~(1 << VS4L_CL_FLAG_HW_TIMEOUT_RECOVERED);
|
|
c->flags &= ~(1 << VS4L_CL_FLAG_HW_TIMEOUT_NOTRECOVERABLE);
|
|
|
|
if (test_bit(VS4L_CL_FLAG_TIMESTAMP, &clist->flags)) {
|
|
c->flags |= (1 << VS4L_CL_FLAG_TIMESTAMP);
|
|
memcpy(c->timestamp, clist->timestamp,
|
|
sizeof(clist->timestamp));
|
|
}
|
|
|
|
if (test_bit(VS4L_CL_FLAG_PREPARE, &bundle->flags))
|
|
c->flags |= (1 << VS4L_CL_FLAG_PREPARE);
|
|
|
|
if (test_bit(VS4L_CL_FLAG_INVALID, &bundle->flags))
|
|
c->flags |= (1 << VS4L_CL_FLAG_INVALID);
|
|
|
|
if (test_bit(VS4L_CL_FLAG_DONE, &bundle->flags))
|
|
c->flags |= (1 << VS4L_CL_FLAG_DONE);
|
|
|
|
if (test_bit(VS4L_CL_FLAG_FW_TIMEOUT, &bundle->flags))
|
|
c->flags |= (1 << VS4L_CL_FLAG_FW_TIMEOUT);
|
|
|
|
if (test_bit(VS4L_CL_FLAG_HW_TIMEOUT_RECOVERED, &bundle->flags))
|
|
c->flags |= (1 << VS4L_CL_FLAG_HW_TIMEOUT_RECOVERED);
|
|
|
|
if (test_bit(VS4L_CL_FLAG_HW_TIMEOUT_NOTRECOVERABLE, &bundle->flags))
|
|
c->flags |= (1 << VS4L_CL_FLAG_HW_TIMEOUT_NOTRECOVERABLE);
|
|
|
|
c->index = clist->index;
|
|
c->id = clist->id;
|
|
if (clist->timestamp[5].tv_usec == 1) {
|
|
c->timestamp[5].tv_sec = clist->timestamp[5].tv_sec; // NPU HW time
|
|
c->timestamp[4].tv_sec = clist->timestamp[4].tv_sec; // DSP HW time
|
|
c->timestamp[4].tv_usec = clist->timestamp[4].tv_usec; // FW total time
|
|
}
|
|
}
|
|
|
|
static void __vb_dqbuf(struct vb_bundle *bundle)
|
|
{
|
|
if (bundle->state == VB_BUF_STATE_DEQUEUED)
|
|
return;
|
|
|
|
bundle->state = VB_BUF_STATE_DEQUEUED;
|
|
}
|
|
|
|
int vb_queue_init(struct vb_queue *q,
|
|
void *alloc_ctx,
|
|
const struct vb2_mem_ops *mem_ops,
|
|
const struct vb_ops *ops,
|
|
struct mutex *lock,
|
|
u32 direction)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (q == NULL) {
|
|
vision_err("vb_queue(q) is NULL\n");
|
|
ret = -EFAULT;
|
|
goto p_err;
|
|
}
|
|
|
|
INIT_LIST_HEAD(&q->queued_list);
|
|
atomic_set(&q->queued_count, 0);
|
|
|
|
INIT_LIST_HEAD(&q->process_list);
|
|
atomic_set(&q->process_count, 0);
|
|
|
|
INIT_LIST_HEAD(&q->done_list);
|
|
atomic_set(&q->done_count, 0);
|
|
|
|
spin_lock_init(&q->done_lock);
|
|
init_waitqueue_head(&q->done_wq);
|
|
|
|
mutex_init(lock);
|
|
|
|
q->num_buffers = 0;
|
|
q->direction = direction;
|
|
q->lock = lock;
|
|
q->streaming = 0;
|
|
q->alloc_ctx = alloc_ctx;
|
|
q->mem_ops = mem_ops;
|
|
q->ops = ops;
|
|
|
|
clear_bit(VB_QUEUE_STATE_FORMAT, &q->state);
|
|
q->format.count = 0;
|
|
q->format.formats = NULL;
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int vb_queue_s_format(struct vb_queue *q, struct vs4l_format_list *flist)
|
|
{
|
|
int ret = 0;
|
|
u32 i;
|
|
struct vs4l_format *f;
|
|
struct vb_fmt *fmt;
|
|
|
|
q->format.count = flist->count;
|
|
q->format.formats = kcalloc(flist->count,
|
|
sizeof(struct vb_format), GFP_KERNEL);
|
|
|
|
if (!q->format.formats) {
|
|
vision_err("q->format.formats is NULL\n");
|
|
ret = -ENOMEM;
|
|
goto p_err;
|
|
}
|
|
|
|
if (q->format.count > VB_MAX_BUFFER) {
|
|
vision_err("flist->count(%d) cannot be greater to VB_MAX_BUFFER(%d)\n", q->format.count, VB_MAX_BUFFER);
|
|
ret = -EINVAL;
|
|
if (q->format.formats)
|
|
kfree(q->format.formats);
|
|
q->format.formats = NULL;
|
|
goto p_err;
|
|
}
|
|
|
|
for (i = 0; i < flist->count; ++i) {
|
|
f = &flist->formats[i];
|
|
|
|
fmt = __vb_find_format(f->format);
|
|
if (!fmt) {
|
|
vision_err("__vb_find_format is fail\n");
|
|
if (q->format.formats)
|
|
kfree(q->format.formats);
|
|
q->format.formats = NULL;
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
q->format.formats[i].fmt = fmt;
|
|
q->format.formats[i].colorspace = f->format;
|
|
q->format.formats[i].target = f->target;
|
|
q->format.formats[i].plane = f->plane;
|
|
q->format.formats[i].width = f->width;
|
|
q->format.formats[i].height = f->height;
|
|
q->format.formats[i].stride = f->stride;
|
|
q->format.formats[i].cstride = f->cstride;
|
|
q->format.formats[i].channels = f->channels;
|
|
q->format.formats[i].pixel_format = f->pixel_format;
|
|
|
|
vision_info("[%u]f : %u, t : %u, p : %u, w : %u, h : %u, s : %u, cs : %u, ch : %u, pf : %u\n",
|
|
i, f->format, f->target, f->plane, f->width, f->height,
|
|
f->stride, f->cstride, f->channels, f->pixel_format);
|
|
|
|
vision_info("[queue] f : %u, t : %u, p : %u, w : %u, h : %u, s : %u, cs : %u, ch : %u, pf : %u\n",
|
|
q->format.formats[i].colorspace, q->format.formats[i].target,
|
|
q->format.formats[i].plane, q->format.formats[i].width,
|
|
q->format.formats[i].height, q->format.formats[i].stride,
|
|
q->format.formats[i].cstride, q->format.formats[i].channels,
|
|
q->format.formats[i].pixel_format);
|
|
|
|
if (q->format.formats[i].plane >= VB_MAX_PLANES) {
|
|
vision_err("f->plane(%d) cannot be greater or equal to VB_MAX_PLANES(%d)\n", q->format.formats[i].plane, VB_MAX_PLANES);
|
|
if (q->format.formats)
|
|
kfree(q->format.formats);
|
|
q->format.formats = NULL;
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
ret = __vb_plane_size(&q->format.formats[i]);
|
|
if (ret) {
|
|
vision_err("__vb_plane_size is fail(%d)\n", ret);
|
|
if (q->format.formats)
|
|
kfree(q->format.formats);
|
|
q->format.formats = NULL;
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
}
|
|
|
|
set_bit(VB_QUEUE_STATE_FORMAT, &q->state);
|
|
|
|
return ret;
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int vb_queue_start(struct vb_queue *q)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!test_bit(VB_QUEUE_STATE_FORMAT, &q->state)) {
|
|
vision_err("format is not configured\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
q->streaming = 1;
|
|
set_bit(VB_QUEUE_STATE_START, &q->state);
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
void __vb_queue_clear(struct vb_queue *q)
|
|
{
|
|
unsigned long flags;
|
|
struct vb_bundle *pos_vb;
|
|
struct vb_bundle *n_vb;
|
|
|
|
BUG_ON(!q);
|
|
|
|
list_for_each_entry_safe(pos_vb, n_vb, &q->queued_list, queued_entry) {
|
|
if (pos_vb->state == VB_BUF_STATE_QUEUED) {
|
|
list_del(&pos_vb->queued_entry);
|
|
atomic_dec(&q->queued_count);
|
|
}
|
|
}
|
|
|
|
spin_lock_irqsave(&q->done_lock, flags);
|
|
list_for_each_entry_safe(pos_vb, n_vb, &q->done_list, done_entry) {
|
|
if (pos_vb->state == VB_BUF_STATE_DONE) {
|
|
list_del(&pos_vb->done_entry);
|
|
atomic_dec(&q->done_count);
|
|
pos_vb->state = VB_BUF_STATE_DEQUEUED;
|
|
list_del(&pos_vb->queued_entry);
|
|
atomic_dec(&q->queued_count);
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&q->done_lock, flags);
|
|
}
|
|
|
|
static int __vb_queue_stop(struct vb_queue *q, int is_forced)
|
|
{
|
|
int ret = 0;
|
|
u32 i;
|
|
struct vb_bundle *bundle;
|
|
|
|
__vb_queue_clear(q);
|
|
|
|
q->streaming = 0;
|
|
wake_up_all(&q->done_wq);
|
|
|
|
if (atomic_read(&q->queued_count) > 0) {
|
|
vision_err("queued list is not empty\n");
|
|
if (!is_forced) {
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
}
|
|
|
|
if (atomic_read(&q->process_count) > 0) {
|
|
vision_err("process list is not empty\n");
|
|
if (!is_forced) {
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
}
|
|
|
|
if (atomic_read(&q->done_count) > 0) {
|
|
vision_err("done list is not empty\n");
|
|
if (!is_forced) {
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
}
|
|
|
|
INIT_LIST_HEAD(&q->queued_list);
|
|
INIT_LIST_HEAD(&q->process_list);
|
|
INIT_LIST_HEAD(&q->done_list);
|
|
|
|
for (i = 0; i < VB_MAX_BUFFER; ++i) {
|
|
bundle = q->bufs[i];
|
|
if (!bundle)
|
|
continue;
|
|
|
|
ret = __vb_buf_unprepare(q, bundle);
|
|
if (ret) {
|
|
vision_err("__vb_buf_unprepare is fail(%d)\n", ret);
|
|
if (!is_forced) {
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
}
|
|
|
|
ret = __vb_queue_free(q, bundle);
|
|
if (ret) {
|
|
vision_err("__vb_queue_free is fail(%d)\n", ret);
|
|
if (!is_forced) {
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
}
|
|
}
|
|
|
|
kfree(q->format.formats);
|
|
|
|
if (q->num_buffers != 0) {
|
|
vision_err("memroy leakage is issued(%d)\n", q->num_buffers);
|
|
BUG();
|
|
}
|
|
clear_bit(VB_QUEUE_STATE_START, &q->state);
|
|
|
|
p_err:
|
|
if (!is_forced)
|
|
return ret;
|
|
else
|
|
return 0; /* Always successful on forced stop */
|
|
}
|
|
|
|
int vb_queue_stop(struct vb_queue *q)
|
|
{
|
|
return __vb_queue_stop(q, 0);
|
|
}
|
|
|
|
int vb_queue_stop_forced(struct vb_queue *q)
|
|
{
|
|
return __vb_queue_stop(q, 1);
|
|
}
|
|
|
|
int vb_queue_qbuf(struct vb_queue *q, struct vs4l_container_list *c)
|
|
{
|
|
int ret = 0;
|
|
struct vb_bundle *bundle;
|
|
struct vb_container *container;
|
|
u32 direction;
|
|
u32 i;
|
|
#if 1 //IS_ENABLED(CONFIG_ION_EXYNOS) || defined(CONFIG_ION_EXYNOS)
|
|
u32 j, k, size;
|
|
#endif
|
|
|
|
if (q->direction != c->direction) {
|
|
vision_err("qbuf: invalid buffer direction\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
if (c->index >= VB_MAX_CONTAINERLIST) {
|
|
vision_err("qbuf: invalid container list index\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
for (i = 0; i < c->count; i++) {
|
|
if (c->containers[i].count > VB_MAX_BUFFER) {
|
|
vision_err("qbuf: Max buffers are %d; passed %d buffers\n",
|
|
VB_MAX_BUFFER, c->containers[i].count);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
}
|
|
|
|
if (c->index >= VB_MAX_BUFFER) {
|
|
vision_err("qbuf: buffer index out of range\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
bundle = q->bufs[c->index];
|
|
if (bundle) {
|
|
ret = __vb_queue_check(bundle, c);
|
|
if (ret) {
|
|
vision_err("__vb_queue_check is fail(%d)\n", ret);
|
|
goto p_err;
|
|
}
|
|
} else {
|
|
ret = __vb_queue_alloc(q, c);
|
|
if (ret) {
|
|
vision_err("__vb_queue_alloc is fail(%d)\n", ret);
|
|
goto p_err;
|
|
}
|
|
bundle = q->bufs[c->index];
|
|
}
|
|
|
|
if (bundle->state != VB_BUF_STATE_DEQUEUED) {
|
|
vision_err("qbuf: buffer already in use\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
ret = __vb_buf_prepare(q, bundle);
|
|
if (ret) {
|
|
vision_err("__vb_buf_prepare is fail(%d)\n", ret);
|
|
goto p_err;
|
|
}
|
|
|
|
if (q->direction == VS4L_DIRECTION_OT)
|
|
direction = DMA_FROM_DEVICE;
|
|
else
|
|
direction = DMA_TO_DEVICE;
|
|
|
|
/* sync buffers */
|
|
for (i = 0; i < bundle->clist.count; ++i) {
|
|
container = &bundle->clist.containers[i];
|
|
BUG_ON(!container->format);
|
|
#if 1 //IS_ENABLED(CONFIG_ION_EXYNOS) || defined(CONFIG_ION_EXYNOS)
|
|
if (container->memory != VS4L_MEMORY_VIRTPTR) {
|
|
k = container->count;
|
|
if (container->format->colorspace == VS4L_DF_IMAGE_NPU ||
|
|
container->format->colorspace == VS4L_DF_IMAGE_DSP)
|
|
size = container->format->size[0];
|
|
else
|
|
size = container->format->size[
|
|
container->format->plane];
|
|
for (j = 0; j < k; ++j) {
|
|
vision_dma_buf_sync(&(container->buffers[j]),
|
|
size, direction, VISION_QBUF);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Add to the queued buffers list, a buffer will stay on it until
|
|
* dequeued in dqbuf.
|
|
*/
|
|
|
|
vision_dbg("qbuf bundle %p, id %d, index %d, count %d\n",
|
|
bundle, c->id, c->index, c->count);
|
|
list_add_tail(&bundle->queued_entry, &q->queued_list);
|
|
bundle->state = VB_BUF_STATE_QUEUED;
|
|
atomic_inc(&q->queued_count);
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int vb_queue_prepare(struct vb_queue *q, struct vs4l_container_list *c)
|
|
{
|
|
int ret = 0;
|
|
struct vb_bundle *bundle;
|
|
u32 i;
|
|
|
|
if (q->direction != c->direction) {
|
|
vision_err("qbuf: invalid buffer direction\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
if (c->index >= VB_MAX_CONTAINERLIST) {
|
|
vision_err("qbuf: invalid container list index\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
for (i = 0; i < c->count; i++) {
|
|
if (c->containers[i].count > VB_MAX_BUFFER) {
|
|
vision_err("qbuf: Max buffers are %d; passed %d buffers\n",
|
|
VB_MAX_BUFFER, c->containers[i].count);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
}
|
|
|
|
if (c->index >= VB_MAX_BUFFER) {
|
|
vision_err("qbuf: buffer index out of range\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
bundle = q->bufs[c->index];
|
|
if (bundle) {
|
|
ret = __vb_queue_check(bundle, c);
|
|
if (ret) {
|
|
vision_err("__vb_queue_check is fail(%d)\n", ret);
|
|
goto p_err;
|
|
}
|
|
} else {
|
|
ret = __vb_queue_alloc(q, c);
|
|
if (ret) {
|
|
vision_err("__vb_queue_alloc is fail(%d)\n", ret);
|
|
goto p_err;
|
|
}
|
|
bundle = q->bufs[c->index];
|
|
}
|
|
|
|
if (bundle->state != VB_BUF_STATE_DEQUEUED) {
|
|
vision_err("qbuf: buffer already in use\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
ret = __vb_buf_prepare(q, bundle);
|
|
if (ret) {
|
|
vision_err("__vb_buf_prepare is fail(%d)\n", ret);
|
|
goto p_err;
|
|
}
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int vb_queue_unprepare(struct vb_queue *q, struct vs4l_container_list *c)
|
|
{
|
|
int ret = 0;
|
|
struct vb_bundle *bundle;
|
|
|
|
if (q->direction != c->direction) {
|
|
vision_err("qbuf: invalid buffer direction\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
if (c->index >= VB_MAX_BUFFER) {
|
|
vision_err("qbuf: buffer index out of range\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
bundle = q->bufs[c->index];
|
|
if (bundle) {
|
|
ret = __vb_queue_check(bundle, c);
|
|
if (ret) {
|
|
vision_err("__vb_queue_check is fail(%d)\n", ret);
|
|
goto p_err;
|
|
}
|
|
|
|
} else {
|
|
vision_err("__vb_bundle doesn't exist(%d)\n", ret);
|
|
ret = -ENOMEM;
|
|
goto p_err;
|
|
}
|
|
|
|
#if 0
|
|
if (bundle->state != VB_BUF_STATE_DEQUEUED) {
|
|
vision_err("qbuf: buffer already in use\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
#endif
|
|
|
|
ret = __vb_buf_unprepare(q, bundle);
|
|
if (ret) {
|
|
vision_err("__vb_buf_prepare is fail(%d)\n", ret);
|
|
goto p_err;
|
|
}
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int vb_queue_dqbuf(struct vb_queue *q,
|
|
struct vs4l_container_list *c,
|
|
bool nonblocking)
|
|
{
|
|
int ret = 0;
|
|
struct vb_bundle *bundle = NULL;
|
|
|
|
if (q->direction != c->direction) {
|
|
vision_err("qbuf: invalid buffer direction\n");
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
ret = __vb_get_done_vb(q, &bundle, c, nonblocking);
|
|
if (ret < 0 || bundle == NULL) {
|
|
if (ret != -EWOULDBLOCK)
|
|
vision_err("__vb_get_done_vb is fail(b %p, r %d)\n", bundle, ret);
|
|
return ret;
|
|
}
|
|
|
|
if (bundle->state != VB_BUF_STATE_DONE) {
|
|
vision_err("dqbuf: Invalid buffer state(%X)\n", bundle->state);
|
|
ret = -EINVAL;
|
|
goto p_err;
|
|
}
|
|
|
|
/* Fill buffer information for the userspace */
|
|
__fill_vs4l_buffer(bundle, c);
|
|
/* Remove from videobuf queue */
|
|
/* go back to dequeued state */
|
|
__vb_dqbuf(bundle);
|
|
|
|
list_del(&bundle->queued_entry);
|
|
atomic_dec(&q->queued_count);
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
void vb_queue_process(struct vb_queue *q, struct vb_bundle *bundle)
|
|
{
|
|
unsigned long flag;
|
|
|
|
BUG_ON(!q);
|
|
BUG_ON(!bundle);
|
|
BUG_ON(q->direction != bundle->clist.direction);
|
|
|
|
/* Temporally add spinlock to avoid list corruption */
|
|
/* Should be improved */
|
|
spin_lock_irqsave(&q->done_lock, flag);
|
|
|
|
bundle->state = VB_BUF_STATE_PROCESS;
|
|
list_add_tail(&bundle->process_entry, &q->process_list);
|
|
atomic_inc(&q->process_count);
|
|
|
|
spin_unlock_irqrestore(&q->done_lock, flag);
|
|
}
|
|
|
|
void vb_queue_done(struct vb_queue *q, struct vb_bundle *bundle)
|
|
{
|
|
struct vb_container *container;
|
|
unsigned long flag;
|
|
u32 direction;
|
|
u32 i;
|
|
#if 1 //IS_ENABLED(CONFIG_ION_EXYNOS) || defined(CONFIG_ION_EXYNOS)
|
|
u32 j, k, size;
|
|
#endif
|
|
|
|
BUG_ON(!q);
|
|
BUG_ON(!bundle);
|
|
BUG_ON(q->direction != bundle->clist.direction);
|
|
|
|
if (q->direction == VS4L_DIRECTION_OT)
|
|
direction = DMA_FROM_DEVICE;
|
|
else
|
|
direction = DMA_TO_DEVICE;
|
|
|
|
/* sync buffers */
|
|
for (i = 0; i < bundle->clist.count; ++i) {
|
|
container = &bundle->clist.containers[i];
|
|
BUG_ON(!container->format);
|
|
#if 1 //IS_ENABLED(CONFIG_ION_EXYNOS) || defined(CONFIG_ION_EXYNOS)
|
|
if (container->memory != VS4L_MEMORY_VIRTPTR) {
|
|
k = container->count;
|
|
if (container->format->colorspace == VS4L_DF_IMAGE_NPU ||
|
|
container->format->colorspace == VS4L_DF_IMAGE_DSP)
|
|
size = container->format->size[0];
|
|
else {
|
|
size = container->format->size[
|
|
container->format->plane];
|
|
}
|
|
for (j = 0; j < k; ++j) {
|
|
/* only for output */
|
|
if (direction == DMA_FROM_DEVICE)
|
|
vision_dma_buf_sync(&(container->buffers[j]),
|
|
size, direction, VISION_DQBUF);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
spin_lock_irqsave(&q->done_lock, flag);
|
|
|
|
list_del(&bundle->process_entry);
|
|
atomic_dec(&q->process_count);
|
|
|
|
bundle->state = VB_BUF_STATE_DONE;
|
|
list_add_tail(&bundle->done_entry, &q->done_list);
|
|
atomic_inc(&q->done_count);
|
|
|
|
spin_unlock_irqrestore(&q->done_lock, flag);
|
|
vision_dbg("done bundle %p, id %d, index %d, count %d\n",
|
|
bundle, bundle->clist.id, bundle->clist.index, bundle->clist.count);
|
|
wake_up(&q->done_wq);
|
|
}
|
|
|
|
void vb_queue_sync(u32 direction, struct vb_container_list *c)
|
|
{
|
|
struct vb_container *container;
|
|
u32 i, j, k, size;
|
|
|
|
/* sync buffers */
|
|
for (i = 0; i < c->count; ++i) {
|
|
container = &c->containers[i];
|
|
BUG_ON(!container->format);
|
|
if (container->memory != VS4L_MEMORY_VIRTPTR) {
|
|
k = container->count;
|
|
if (container->format->colorspace == VS4L_DF_IMAGE_NPU ||
|
|
container->format->colorspace == VS4L_DF_IMAGE_DSP)
|
|
size = container->format->size[0];
|
|
else
|
|
size = container->format->size[
|
|
container->format->plane];
|
|
for (j = 0; j < k; ++j) {
|
|
vision_dma_buf_sync(&(container->buffers[j]),
|
|
size, direction, VISION_QBUF);
|
|
}
|
|
}
|
|
}
|
|
}
|