// SPDX-License-Identifier: GPL-2.0-only
/*
* vivid-vid-out.c - video output support functions.
*
* Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*/
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/videodev2.h>
#include <linux/v4l2-dv-timings.h>
#include <media/v4l2-common.h>
#include <media/v4l2-event.h>
#include <media/v4l2-dv-timings.h>
#include <media/v4l2-rect.h>
#include "vivid-core.h"
#include "vivid-vid-common.h"
#include "vivid-kthread-out.h"
#include "vivid-vid-out.h"
static int vid_out_queue_setup(struct vb2_queue *vq,
unsigned *nbuffers, unsigned *nplanes,
unsigned sizes[], struct device *alloc_devs[])
{
struct vivid_dev *dev = vb2_get_drv_priv(vq);
const struct vivid_fmt *vfmt = dev->fmt_out;
unsigned planes = vfmt->buffers;
unsigned h = dev->fmt_out_rect.height;
unsigned int size = dev->bytesperline_out[0] * h + vfmt->data_offset[0];
unsigned p;
for (p = vfmt->buffers; p < vfmt->planes; p++)
size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p] +
vfmt->data_offset[p];
if (dev->field_out == V4L2_FIELD_ALTERNATE) {
/*
* You cannot use write() with FIELD_ALTERNATE since the field
* information (TOP/BOTTOM) cannot be passed to the kernel.
*/
if (vb2_fileio_is_active(vq))
return -EINVAL;
}
if (dev->queue_setup_error) {
/*
* Error injection: test what happens if queue_setup() returns
* an error.
*/
dev->queue_setup_error = false;
return -EINVAL;
}
if (*nplanes) {
/*
* Check if the number of requested planes match
* the number of planes in the current format. You can't mix that.
*/
if (*nplanes != planes)
return -EINVAL;
if (sizes[0] < size)
return -EINVAL;
for (p = 1; p < planes; p++) {
if (sizes[p] < dev->bytesperline_out[p] * h /
vfmt->vdownsampling[p] +
vfmt->data_offset[p])
return -EINVAL;
}
} else {
for (p = 0; p < planes; p++)
sizes[p] = p ? dev->bytesperline_out[p] * h /
vfmt->vdownsampling[p] +
vfmt->data_offset[p] : size;
}
*nplanes = planes;
dprintk(dev, 1, "%s: count=%u\n", __func__, *nbuffers);
for (p = 0; p < planes; p++)
dprintk(dev, 1, "%s: size[%u]=%u\n", __func__, p, sizes[p]);
return 0;
}
static int vid_out_buf_out_validate(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
dprintk(dev, 1, "%s\n", __func__);
if (dev->field_out != V4L2_FIELD_ALTERNATE)
vbuf->field = dev->field_out;
else if (vbuf->field != V4L2_FIELD_TOP &&
vbuf->field != V4L2_FIELD_BOTTOM)
return -EINVAL;
return 0;
}
static int vid_out_buf_prepare(struct vb2_buffer *vb)
{
struct vivid_dev *dev = vb2_get_drv_priv(vb->vb2_queue);
const struct vivid_fmt *vfmt = dev->fmt_out;
unsigned int planes = vfmt->buffers;
unsigned int h = dev->fmt_out_rect.height;
unsigned int size = dev->bytesperline_out[0] * h;
unsigned p;
for (p = vfmt->buffers; p < vfmt->planes; p++)
size += dev->bytesperline_out[p] * h / vfmt->vdownsampling[p];
dprintk(dev, 1, "%s\n", __func__);
if (WARN_ON(NULL == dev->fmt_out))
return -EINVAL;
if (dev->buf_prepare_error) {
/*
* Error injection: test what happens if buf_prepare() returns
* an error.
*/
dev->buf_prepare_error = false;
return -EINVAL;