alistair23-linux/drivers/media/dvb-core/dvb_vb2.c
Linus Torvalds b3491d8430 media updates for v4.20-rc1
-----BEGIN PGP SIGNATURE-----
 
 iQIcBAABAgAGBQJb2F9AAAoJEAhfPr2O5OEVm5YP/Ak53aAEI1oJNequwdTYKc+/
 2xWRpYWREa1g+x4MlqWO+szlPWbGXCUVwye8ii2N/xihLapsKVrLCr/dDd5khsvw
 bDux33BzpU3Ug/ncQKD6ZZv4vVRzG8DMPcpkOwSs0OoboJns6AkHVGCugR32qZsH
 3SH/r1aJce0oK1rrzgbYYZHTvaPshvY2IOLPKrtFmO+73iCVRhpSdWjFsY+q2Alp
 +3Ho/06iQYB2i+enXrwoIKHAYoXArXYbxS2dhaNz+NURrOAytmgfMisvvt67heHx
 IEilE0AcSjjlN/eyOxp+WCZrg9JLXVzZLX6ZnqqM2OEu1AS/XBultJBsGaN0hOiV
 dir2enoHNNOStI40hNSdbumg9I0Txmag2jtpaGyaBnnGmGRJ/JIYegCPRVMLygAf
 HHFHjR4fnRnqZrlh9OGAHaqc9RNlUgFVdlyqFtdyIah+aNeuij3o69mWM35QMLhw
 /0dTXBUXw9aD1dEg1cZ6PdzLWJgDd7n1gIdfzzzzLnzmBwmmhqxW8+evu9qSAXsP
 rnEZuE77HYKVfiacWMwpZK6+lT51STAE8ouo3N8fmaC+4RQmpq0dYXtR8RnlcSUD
 hKpJ6UsIIb5A6xKX7ed8x6FxV14TEEaa042A4eclxsAFiqqkNfWSozqV0vfW5vCD
 2lrsuN3knpfh7XDBSr0y
 =V4X4
 -----END PGP SIGNATURE-----

Merge tag 'media/v4.20-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media

Pull new experimental media request API from Mauro Carvalho Chehab:
 "A new media request API

  This API is needed to support device drivers that can dynamically
  change their parameters for each new frame. The latest versions of
  Google camera and codec HAL depends on such feature.

  At this stage, it supports only stateless codecs.

  It has been discussed for a long time (at least over the last 3-4
  years), and we finally reached to something that seem to work.

  This series contain both the API and core changes required to support
  it and a new m2m decoder driver (cedrus).

  As the current API is still experimental, the only real driver using
  it (cedrus) was added at staging[1]. We intend to keep it there for a
  while, in order to test the API. Only when we're sure that this API
  works for other cases (like encoders), we'll move this driver out of
  staging and set the API into a stone.

  [1] We added support for the vivid virtual driver (used only for
  testing) to it too, as it makes easier to test the API for the ones
  that don't have the cedrus hardware"

* tag 'media/v4.20-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (53 commits)
  media: dt-bindings: Document the Rockchip VPU bindings
  media: platform: Add Cedrus VPU decoder driver
  media: dt-bindings: media: Document bindings for the Cedrus VPU driver
  media: v4l: Add definition for the Sunxi tiled NV12 format
  media: v4l: Add definitions for MPEG-2 slice format and metadata
  media: videobuf2-core: Rework and rename helper for request buffer count
  media: v4l2-ctrls.c: initialize an error return code with zero
  media: v4l2-compat-ioctl32.c: add missing documentation for a field
  media: media-request: update documentation
  media: media-request: EPERM -> EACCES/EBUSY
  media: v4l2-ctrls: improve media_request_(un)lock_for_update
  media: v4l2-ctrls: use media_request_(un)lock_for_access
  media: media-request: add media_request_(un)lock_for_access
  media: vb2: set reqbufs/create_bufs capabilities
  media: videodev2.h: add new capabilities for buffer types
  media: buffer.rst: only set V4L2_BUF_FLAG_REQUEST_FD for QBUF
  media: v4l2-ctrls: return -EACCES if request wasn't completed
  media: media-request: return -EINVAL for invalid request_fds
  media: vivid: add request support
  media: vivid: add mc
  ...
2018-10-31 10:53:29 -07:00

443 lines
10 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* dvb-vb2.c - dvb-vb2
*
* Copyright (C) 2015 Samsung Electronics
*
* Author: jh1009.sung@samsung.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.
*/
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <media/dvbdev.h>
#include <media/dvb_vb2.h>
#define DVB_V2_MAX_SIZE (4096 * 188)
static int vb2_debug;
module_param(vb2_debug, int, 0644);
#define dprintk(level, fmt, arg...) \
do { \
if (vb2_debug >= level) \
pr_info("vb2: %s: " fmt, __func__, ## arg); \
} while (0)
static int _queue_setup(struct vb2_queue *vq,
unsigned int *nbuffers, unsigned int *nplanes,
unsigned int sizes[], struct device *alloc_devs[])
{
struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq);
ctx->buf_cnt = *nbuffers;
*nplanes = 1;
sizes[0] = ctx->buf_siz;
/*
* videobuf2-vmalloc allocator is context-less so no need to set
* alloc_ctxs array.
*/
dprintk(3, "[%s] count=%d, size=%d\n", ctx->name,
*nbuffers, sizes[0]);
return 0;
}
static int _buffer_prepare(struct vb2_buffer *vb)
{
struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
unsigned long size = ctx->buf_siz;
if (vb2_plane_size(vb, 0) < size) {
dprintk(1, "[%s] data will not fit into plane (%lu < %lu)\n",
ctx->name, vb2_plane_size(vb, 0), size);
return -EINVAL;
}
vb2_set_plane_payload(vb, 0, size);
dprintk(3, "[%s]\n", ctx->name);
return 0;
}
static void _buffer_queue(struct vb2_buffer *vb)
{
struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct dvb_buffer *buf = container_of(vb, struct dvb_buffer, vb);
unsigned long flags = 0;
spin_lock_irqsave(&ctx->slock, flags);
list_add_tail(&buf->list, &ctx->dvb_q);
spin_unlock_irqrestore(&ctx->slock, flags);
dprintk(3, "[%s]\n", ctx->name);
}
static int _start_streaming(struct vb2_queue *vq, unsigned int count)
{
struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq);
dprintk(3, "[%s] count=%d\n", ctx->name, count);
return 0;
}
static void _stop_streaming(struct vb2_queue *vq)
{
struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq);
struct dvb_buffer *buf;
unsigned long flags = 0;
dprintk(3, "[%s]\n", ctx->name);
spin_lock_irqsave(&ctx->slock, flags);
while (!list_empty(&ctx->dvb_q)) {
buf = list_entry(ctx->dvb_q.next,
struct dvb_buffer, list);
vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR);
list_del(&buf->list);
}
spin_unlock_irqrestore(&ctx->slock, flags);
}
static void _dmxdev_lock(struct vb2_queue *vq)
{
struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq);
mutex_lock(&ctx->mutex);
dprintk(3, "[%s]\n", ctx->name);
}
static void _dmxdev_unlock(struct vb2_queue *vq)
{
struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vq);
if (mutex_is_locked(&ctx->mutex))
mutex_unlock(&ctx->mutex);
dprintk(3, "[%s]\n", ctx->name);
}
static const struct vb2_ops dvb_vb2_qops = {
.queue_setup = _queue_setup,
.buf_prepare = _buffer_prepare,
.buf_queue = _buffer_queue,
.start_streaming = _start_streaming,
.stop_streaming = _stop_streaming,
.wait_prepare = _dmxdev_unlock,
.wait_finish = _dmxdev_lock,
};
static void _fill_dmx_buffer(struct vb2_buffer *vb, void *pb)
{
struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct dmx_buffer *b = pb;
b->index = vb->index;
b->length = vb->planes[0].length;
b->bytesused = vb->planes[0].bytesused;
b->offset = vb->planes[0].m.offset;
dprintk(3, "[%s]\n", ctx->name);
}
static int _fill_vb2_buffer(struct vb2_buffer *vb, struct vb2_plane *planes)
{
struct dvb_vb2_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
planes[0].bytesused = 0;
dprintk(3, "[%s]\n", ctx->name);
return 0;
}
static const struct vb2_buf_ops dvb_vb2_buf_ops = {
.fill_user_buffer = _fill_dmx_buffer,
.fill_vb2_buffer = _fill_vb2_buffer,
};
/*
* Videobuf operations
*/
int dvb_vb2_init(struct dvb_vb2_ctx *ctx, const char *name, int nonblocking)
{
struct vb2_queue *q = &ctx->vb_q;
int ret;
memset(ctx, 0, sizeof(struct dvb_vb2_ctx));
q->type = DVB_BUF_TYPE_CAPTURE;
/**capture type*/
q->is_output = 0;
/**only mmap is supported currently*/
q->io_modes = VB2_MMAP;
q->drv_priv = ctx;
q->buf_struct_size = sizeof(struct dvb_buffer);
q->min_buffers_needed = 1;
q->ops = &dvb_vb2_qops;
q->mem_ops = &vb2_vmalloc_memops;
q->buf_ops = &dvb_vb2_buf_ops;
q->num_buffers = 0;
ret = vb2_core_queue_init(q);
if (ret) {
ctx->state = DVB_VB2_STATE_NONE;
dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
return ret;
}
mutex_init(&ctx->mutex);
spin_lock_init(&ctx->slock);
INIT_LIST_HEAD(&ctx->dvb_q);
strscpy(ctx->name, name, DVB_VB2_NAME_MAX);
ctx->nonblocking = nonblocking;
ctx->state = DVB_VB2_STATE_INIT;
dprintk(3, "[%s]\n", ctx->name);
return 0;
}
int dvb_vb2_release(struct dvb_vb2_ctx *ctx)
{
struct vb2_queue *q = (struct vb2_queue *)&ctx->vb_q;
if (ctx->state & DVB_VB2_STATE_INIT)
vb2_core_queue_release(q);
ctx->state = DVB_VB2_STATE_NONE;
dprintk(3, "[%s]\n", ctx->name);
return 0;
}
int dvb_vb2_stream_on(struct dvb_vb2_ctx *ctx)
{
struct vb2_queue *q = &ctx->vb_q;
int ret;
ret = vb2_core_streamon(q, q->type);
if (ret) {
ctx->state = DVB_VB2_STATE_NONE;
dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
return ret;
}
ctx->state |= DVB_VB2_STATE_STREAMON;
dprintk(3, "[%s]\n", ctx->name);
return 0;
}
int dvb_vb2_stream_off(struct dvb_vb2_ctx *ctx)
{
struct vb2_queue *q = (struct vb2_queue *)&ctx->vb_q;
int ret;
ctx->state &= ~DVB_VB2_STATE_STREAMON;
ret = vb2_core_streamoff(q, q->type);
if (ret) {
ctx->state = DVB_VB2_STATE_NONE;
dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
return ret;
}
dprintk(3, "[%s]\n", ctx->name);
return 0;
}
int dvb_vb2_is_streaming(struct dvb_vb2_ctx *ctx)
{
return (ctx->state & DVB_VB2_STATE_STREAMON);
}
int dvb_vb2_fill_buffer(struct dvb_vb2_ctx *ctx,
const unsigned char *src, int len,
enum dmx_buffer_flags *buffer_flags)
{
unsigned long flags = 0;
void *vbuf = NULL;
int todo = len;
unsigned char *psrc = (unsigned char *)src;
int ll = 0;
/*
* normal case: This func is called twice from demux driver
* one with valid src pointer, second time with NULL pointer
*/
if (!src || !len)
return 0;
spin_lock_irqsave(&ctx->slock, flags);
if (buffer_flags && *buffer_flags) {
ctx->flags |= *buffer_flags;
*buffer_flags = 0;
}
while (todo) {
if (!ctx->buf) {
if (list_empty(&ctx->dvb_q)) {
dprintk(3, "[%s] Buffer overflow!!!\n",
ctx->name);
break;
}
ctx->buf = list_entry(ctx->dvb_q.next,
struct dvb_buffer, list);
ctx->remain = vb2_plane_size(&ctx->buf->vb, 0);
ctx->offset = 0;
}
if (!dvb_vb2_is_streaming(ctx)) {
vb2_buffer_done(&ctx->buf->vb, VB2_BUF_STATE_ERROR);
list_del(&ctx->buf->list);
ctx->buf = NULL;
break;
}
/* Fill buffer */
ll = min(todo, ctx->remain);
vbuf = vb2_plane_vaddr(&ctx->buf->vb, 0);
memcpy(vbuf + ctx->offset, psrc, ll);
todo -= ll;
psrc += ll;
ctx->remain -= ll;
ctx->offset += ll;
if (ctx->remain == 0) {
vb2_buffer_done(&ctx->buf->vb, VB2_BUF_STATE_DONE);
list_del(&ctx->buf->list);
ctx->buf = NULL;
}
}
if (ctx->nonblocking && ctx->buf) {
vb2_set_plane_payload(&ctx->buf->vb, 0, ll);
vb2_buffer_done(&ctx->buf->vb, VB2_BUF_STATE_DONE);
list_del(&ctx->buf->list);
ctx->buf = NULL;
}
spin_unlock_irqrestore(&ctx->slock, flags);
if (todo)
dprintk(1, "[%s] %d bytes are dropped.\n", ctx->name, todo);
else
dprintk(3, "[%s]\n", ctx->name);
dprintk(3, "[%s] %d bytes are copied\n", ctx->name, len - todo);
return (len - todo);
}
int dvb_vb2_reqbufs(struct dvb_vb2_ctx *ctx, struct dmx_requestbuffers *req)
{
int ret;
/* Adjust size to a sane value */
if (req->size > DVB_V2_MAX_SIZE)
req->size = DVB_V2_MAX_SIZE;
/* FIXME: round req->size to a 188 or 204 multiple */
ctx->buf_siz = req->size;
ctx->buf_cnt = req->count;
ret = vb2_core_reqbufs(&ctx->vb_q, VB2_MEMORY_MMAP, &req->count);
if (ret) {
ctx->state = DVB_VB2_STATE_NONE;
dprintk(1, "[%s] count=%d size=%d errno=%d\n", ctx->name,
ctx->buf_cnt, ctx->buf_siz, ret);
return ret;
}
ctx->state |= DVB_VB2_STATE_REQBUFS;
dprintk(3, "[%s] count=%d size=%d\n", ctx->name,
ctx->buf_cnt, ctx->buf_siz);
return 0;
}
int dvb_vb2_querybuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
{
vb2_core_querybuf(&ctx->vb_q, b->index, b);
dprintk(3, "[%s] index=%d\n", ctx->name, b->index);
return 0;
}
int dvb_vb2_expbuf(struct dvb_vb2_ctx *ctx, struct dmx_exportbuffer *exp)
{
struct vb2_queue *q = &ctx->vb_q;
int ret;
ret = vb2_core_expbuf(&ctx->vb_q, &exp->fd, q->type, exp->index,
0, exp->flags);
if (ret) {
dprintk(1, "[%s] index=%d errno=%d\n", ctx->name,
exp->index, ret);
return ret;
}
dprintk(3, "[%s] index=%d fd=%d\n", ctx->name, exp->index, exp->fd);
return 0;
}
int dvb_vb2_qbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
{
int ret;
ret = vb2_core_qbuf(&ctx->vb_q, b->index, b, NULL);
if (ret) {
dprintk(1, "[%s] index=%d errno=%d\n", ctx->name,
b->index, ret);
return ret;
}
dprintk(5, "[%s] index=%d\n", ctx->name, b->index);
return 0;
}
int dvb_vb2_dqbuf(struct dvb_vb2_ctx *ctx, struct dmx_buffer *b)
{
unsigned long flags;
int ret;
ret = vb2_core_dqbuf(&ctx->vb_q, &b->index, b, ctx->nonblocking);
if (ret) {
dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
return ret;
}
spin_lock_irqsave(&ctx->slock, flags);
b->count = ctx->count++;
b->flags = ctx->flags;
ctx->flags = 0;
spin_unlock_irqrestore(&ctx->slock, flags);
dprintk(5, "[%s] index=%d, count=%d, flags=%d\n",
ctx->name, b->index, ctx->count, b->flags);
return 0;
}
int dvb_vb2_mmap(struct dvb_vb2_ctx *ctx, struct vm_area_struct *vma)
{
int ret;
ret = vb2_mmap(&ctx->vb_q, vma);
if (ret) {
dprintk(1, "[%s] errno=%d\n", ctx->name, ret);
return ret;
}
dprintk(3, "[%s] ret=%d\n", ctx->name, ret);
return 0;
}
__poll_t dvb_vb2_poll(struct dvb_vb2_ctx *ctx, struct file *file,
poll_table *wait)
{
dprintk(3, "[%s]\n", ctx->name);
return vb2_core_poll(&ctx->vb_q, file, wait);
}