linux_dsm_epyc7002/drivers/media/platform/exynos4-is/fimc-isp-video.c
Sakari Ailus 20b8522736 [media] media: Rename graph and pipeline structs and functions
The media_entity_pipeline_start() and media_entity_pipeline_stop()
functions are renamed as media_pipeline_start() and media_pipeline_stop(),
respectively. The reason is two-fold: the pipeline struct is, rightly,
already called media_pipeline (rather than media_entity_pipeline) and what
this really is about is a pipeline. A pipeline consists of entities ---
and, well, other objects embedded in these entities.

As the pipeline object will be in the future moved from entities to pads
in order to support multiple pipelines through a single entity, do the
renaming now.

Similarly, functions operating on struct media_entity_graph as well as the
struct itself are renamed by dropping the "entity_" part from the prefix
of the function family and the data structure. The graph traversal which
is what the functions are about is not specifically about entities only
and will operate on pads for the same reason as the media pipeline.

The patch has been generated using the following command:

git grep -l media_entity |xargs perl -i -pe '
	s/media_entity_pipeline/media_pipeline/g;
	s/media_entity_graph/media_graph/g'

And a few manual edits related to line start alignment and line wrapping.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2017-01-27 16:13:24 -02:00

654 lines
17 KiB
C

/*
* Samsung EXYNOS4x12 FIMC-IS (Imaging Subsystem) driver
*
* FIMC-IS ISP video input and video output DMA interface driver
*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
*
* The hardware handling code derived from a driver written by
* Younghwan Joo <yhwan.joo@samsung.com>.
*
* 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/bitops.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/printk.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include <media/videobuf2-v4l2.h>
#include <media/videobuf2-dma-contig.h>
#include <media/drv-intf/exynos-fimc.h>
#include "common.h"
#include "media-dev.h"
#include "fimc-is.h"
#include "fimc-isp-video.h"
#include "fimc-is-param.h"
static int isp_video_capture_queue_setup(struct vb2_queue *vq,
unsigned int *num_buffers, unsigned int *num_planes,
unsigned int sizes[], struct device *alloc_devs[])
{
struct fimc_isp *isp = vb2_get_drv_priv(vq);
struct v4l2_pix_format_mplane *vid_fmt = &isp->video_capture.pixfmt;
const struct fimc_fmt *fmt = isp->video_capture.format;
unsigned int wh, i;
wh = vid_fmt->width * vid_fmt->height;
if (fmt == NULL)
return -EINVAL;
*num_buffers = clamp_t(u32, *num_buffers, FIMC_ISP_REQ_BUFS_MIN,
FIMC_ISP_REQ_BUFS_MAX);
if (*num_planes) {
if (*num_planes != fmt->memplanes)
return -EINVAL;
for (i = 0; i < *num_planes; i++)
if (sizes[i] < (wh * fmt->depth[i]) / 8)
return -EINVAL;
return 0;
}
*num_planes = fmt->memplanes;
for (i = 0; i < fmt->memplanes; i++)
sizes[i] = (wh * fmt->depth[i]) / 8;
return 0;
}
static inline struct param_dma_output *__get_isp_dma2(struct fimc_is *is)
{
return &__get_curr_is_config(is)->isp.dma2_output;
}
static int isp_video_capture_start_streaming(struct vb2_queue *q,
unsigned int count)
{
struct fimc_isp *isp = vb2_get_drv_priv(q);
struct fimc_is *is = fimc_isp_to_is(isp);
struct param_dma_output *dma = __get_isp_dma2(is);
struct fimc_is_video *video = &isp->video_capture;
int ret;
if (!test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state) ||
test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state))
return 0;
dma->cmd = DMA_OUTPUT_COMMAND_ENABLE;
dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_ENABLE;
dma->buffer_address = is->is_dma_p_region +
DMA2_OUTPUT_ADDR_ARRAY_OFFS;
dma->buffer_number = video->reqbufs_count;
dma->dma_out_mask = video->buf_mask;
isp_dbg(2, &video->ve.vdev,
"buf_count: %d, planes: %d, dma addr table: %#x\n",
video->buf_count, video->format->memplanes,
dma->buffer_address);
fimc_is_mem_barrier();
fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT);
__fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT);
ret = fimc_is_itf_s_param(is, false);
if (ret < 0)
return ret;
ret = fimc_pipeline_call(&video->ve, set_stream, 1);
if (ret < 0)
return ret;
set_bit(ST_ISP_VID_CAP_STREAMING, &isp->state);
return ret;
}
static void isp_video_capture_stop_streaming(struct vb2_queue *q)
{
struct fimc_isp *isp = vb2_get_drv_priv(q);
struct fimc_is *is = fimc_isp_to_is(isp);
struct param_dma_output *dma = __get_isp_dma2(is);
int ret;
ret = fimc_pipeline_call(&isp->video_capture.ve, set_stream, 0);
if (ret < 0)
return;
dma->cmd = DMA_OUTPUT_COMMAND_DISABLE;
dma->notify_dma_done = DMA_OUTPUT_NOTIFY_DMA_DONE_DISABLE;
dma->buffer_number = 0;
dma->buffer_address = 0;
dma->dma_out_mask = 0;
fimc_is_set_param_bit(is, PARAM_ISP_DMA2_OUTPUT);
__fimc_is_hw_update_param(is, PARAM_ISP_DMA2_OUTPUT);
ret = fimc_is_itf_s_param(is, false);
if (ret < 0)
dev_warn(&is->pdev->dev, "%s: DMA stop failed\n", __func__);
fimc_is_hw_set_isp_buf_mask(is, 0);
clear_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state);
clear_bit(ST_ISP_VID_CAP_STREAMING, &isp->state);
isp->video_capture.buf_count = 0;
}
static int isp_video_capture_buffer_prepare(struct vb2_buffer *vb)
{
struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue);
struct fimc_is_video *video = &isp->video_capture;
int i;
if (video->format == NULL)
return -EINVAL;
for (i = 0; i < video->format->memplanes; i++) {
unsigned long size = video->pixfmt.plane_fmt[i].sizeimage;
if (vb2_plane_size(vb, i) < size) {
v4l2_err(&video->ve.vdev,
"User buffer too small (%ld < %ld)\n",
vb2_plane_size(vb, i), size);
return -EINVAL;
}
vb2_set_plane_payload(vb, i, size);
}
/* Check if we get one of the already known buffers. */
if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) {
dma_addr_t dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0);
int i;
for (i = 0; i < video->buf_count; i++)
if (video->buffers[i]->dma_addr[0] == dma_addr)
return 0;
return -ENXIO;
}
return 0;
}
static void isp_video_capture_buffer_queue(struct vb2_buffer *vb)
{
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
struct fimc_isp *isp = vb2_get_drv_priv(vb->vb2_queue);
struct fimc_is_video *video = &isp->video_capture;
struct fimc_is *is = fimc_isp_to_is(isp);
struct isp_video_buf *ivb = to_isp_video_buf(vbuf);
unsigned long flags;
unsigned int i;
if (test_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state)) {
spin_lock_irqsave(&is->slock, flags);
video->buf_mask |= BIT(ivb->index);
spin_unlock_irqrestore(&is->slock, flags);
} else {
unsigned int num_planes = video->format->memplanes;
ivb->index = video->buf_count;
video->buffers[ivb->index] = ivb;
for (i = 0; i < num_planes; i++) {
int buf_index = ivb->index * num_planes + i;
ivb->dma_addr[i] = vb2_dma_contig_plane_dma_addr(vb, i);
is->is_p_region->shared[32 + buf_index] =
ivb->dma_addr[i];
isp_dbg(2, &video->ve.vdev,
"dma_buf %d (%d/%d/%d) addr: %pad\n",
buf_index, ivb->index, i, vb->index,
&ivb->dma_addr[i]);
}
if (++video->buf_count < video->reqbufs_count)
return;
video->buf_mask = (1UL << video->buf_count) - 1;
set_bit(ST_ISP_VID_CAP_BUF_PREP, &isp->state);
}
if (!test_bit(ST_ISP_VID_CAP_STREAMING, &isp->state))
isp_video_capture_start_streaming(vb->vb2_queue, 0);
}
/*
* FIMC-IS ISP input and output DMA interface interrupt handler.
* Locking: called with is->slock spinlock held.
*/
void fimc_isp_video_irq_handler(struct fimc_is *is)
{
struct fimc_is_video *video = &is->isp.video_capture;
struct vb2_v4l2_buffer *vbuf;
int buf_index;
/* TODO: Ensure the DMA is really stopped in stop_streaming callback */
if (!test_bit(ST_ISP_VID_CAP_STREAMING, &is->isp.state))
return;
buf_index = (is->i2h_cmd.args[1] - 1) % video->buf_count;
vbuf = &video->buffers[buf_index]->vb;
vbuf->vb2_buf.timestamp = ktime_get_ns();
vb2_buffer_done(&vbuf->vb2_buf, VB2_BUF_STATE_DONE);
video->buf_mask &= ~BIT(buf_index);
fimc_is_hw_set_isp_buf_mask(is, video->buf_mask);
}
static const struct vb2_ops isp_video_capture_qops = {
.queue_setup = isp_video_capture_queue_setup,
.buf_prepare = isp_video_capture_buffer_prepare,
.buf_queue = isp_video_capture_buffer_queue,
.wait_prepare = vb2_ops_wait_prepare,
.wait_finish = vb2_ops_wait_finish,
.start_streaming = isp_video_capture_start_streaming,
.stop_streaming = isp_video_capture_stop_streaming,
};
static int isp_video_open(struct file *file)
{
struct fimc_isp *isp = video_drvdata(file);
struct exynos_video_entity *ve = &isp->video_capture.ve;
struct media_entity *me = &ve->vdev.entity;
int ret;
if (mutex_lock_interruptible(&isp->video_lock))
return -ERESTARTSYS;
ret = v4l2_fh_open(file);
if (ret < 0)
goto unlock;
ret = pm_runtime_get_sync(&isp->pdev->dev);
if (ret < 0)
goto rel_fh;
if (v4l2_fh_is_singular_file(file)) {
mutex_lock(&me->graph_obj.mdev->graph_mutex);
ret = fimc_pipeline_call(ve, open, me, true);
/* Mark the video pipeline as in use. */
if (ret == 0)
me->use_count++;
mutex_unlock(&me->graph_obj.mdev->graph_mutex);
}
if (!ret)
goto unlock;
rel_fh:
v4l2_fh_release(file);
unlock:
mutex_unlock(&isp->video_lock);
return ret;
}
static int isp_video_release(struct file *file)
{
struct fimc_isp *isp = video_drvdata(file);
struct fimc_is_video *ivc = &isp->video_capture;
struct media_entity *entity = &ivc->ve.vdev.entity;
struct media_device *mdev = entity->graph_obj.mdev;
mutex_lock(&isp->video_lock);
if (v4l2_fh_is_singular_file(file) && ivc->streaming) {
media_pipeline_stop(entity);
ivc->streaming = 0;
}
vb2_fop_release(file);
if (v4l2_fh_is_singular_file(file)) {
fimc_pipeline_call(&ivc->ve, close);
mutex_lock(&mdev->graph_mutex);
entity->use_count--;
mutex_unlock(&mdev->graph_mutex);
}
pm_runtime_put(&isp->pdev->dev);
mutex_unlock(&isp->video_lock);
return 0;
}
static const struct v4l2_file_operations isp_video_fops = {
.owner = THIS_MODULE,
.open = isp_video_open,
.release = isp_video_release,
.poll = vb2_fop_poll,
.unlocked_ioctl = video_ioctl2,
.mmap = vb2_fop_mmap,
};
/*
* Video node ioctl operations
*/
static int isp_video_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
struct fimc_isp *isp = video_drvdata(file);
__fimc_vidioc_querycap(&isp->pdev->dev, cap, V4L2_CAP_STREAMING);
return 0;
}
static int isp_video_enum_fmt_mplane(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
const struct fimc_fmt *fmt;
if (f->index >= FIMC_ISP_NUM_FORMATS)
return -EINVAL;
fmt = fimc_isp_find_format(NULL, NULL, f->index);
if (WARN_ON(fmt == NULL))
return -EINVAL;
strlcpy(f->description, fmt->name, sizeof(f->description));
f->pixelformat = fmt->fourcc;
return 0;
}
static int isp_video_g_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct fimc_isp *isp = video_drvdata(file);
f->fmt.pix_mp = isp->video_capture.pixfmt;
return 0;
}
static void __isp_video_try_fmt(struct fimc_isp *isp,
struct v4l2_pix_format_mplane *pixm,
const struct fimc_fmt **fmt)
{
*fmt = fimc_isp_find_format(&pixm->pixelformat, NULL, 2);
pixm->colorspace = V4L2_COLORSPACE_SRGB;
pixm->field = V4L2_FIELD_NONE;
pixm->num_planes = (*fmt)->memplanes;
pixm->pixelformat = (*fmt)->fourcc;
/*
* TODO: double check with the docmentation these width/height
* constraints are correct.
*/
v4l_bound_align_image(&pixm->width, FIMC_ISP_SOURCE_WIDTH_MIN,
FIMC_ISP_SOURCE_WIDTH_MAX, 3,
&pixm->height, FIMC_ISP_SOURCE_HEIGHT_MIN,
FIMC_ISP_SOURCE_HEIGHT_MAX, 0, 0);
}
static int isp_video_try_fmt_mplane(struct file *file, void *fh,
struct v4l2_format *f)
{
struct fimc_isp *isp = video_drvdata(file);
__isp_video_try_fmt(isp, &f->fmt.pix_mp, NULL);
return 0;
}
static int isp_video_s_fmt_mplane(struct file *file, void *priv,
struct v4l2_format *f)
{
struct fimc_isp *isp = video_drvdata(file);
struct fimc_is *is = fimc_isp_to_is(isp);
struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp;
const struct fimc_fmt *ifmt = NULL;
struct param_dma_output *dma = __get_isp_dma2(is);
__isp_video_try_fmt(isp, pixm, &ifmt);
if (WARN_ON(ifmt == NULL))
return -EINVAL;
dma->format = DMA_OUTPUT_FORMAT_BAYER;
dma->order = DMA_OUTPUT_ORDER_GB_BG;
dma->plane = ifmt->memplanes;
dma->bitwidth = ifmt->depth[0];
dma->width = pixm->width;
dma->height = pixm->height;
fimc_is_mem_barrier();
isp->video_capture.format = ifmt;
isp->video_capture.pixfmt = *pixm;
return 0;
}
/*
* Check for source/sink format differences at each link.
* Return 0 if the formats match or -EPIPE otherwise.
*/
static int isp_video_pipeline_validate(struct fimc_isp *isp)
{
struct v4l2_subdev *sd = &isp->subdev;
struct v4l2_subdev_format sink_fmt, src_fmt;
struct media_pad *pad;
int ret;
while (1) {
/* Retrieve format at the sink pad */
pad = &sd->entity.pads[0];
if (!(pad->flags & MEDIA_PAD_FL_SINK))
break;
sink_fmt.pad = pad->index;
sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sink_fmt);
if (ret < 0 && ret != -ENOIOCTLCMD)
return -EPIPE;
/* Retrieve format at the source pad */
pad = media_entity_remote_pad(pad);
if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
break;
sd = media_entity_to_v4l2_subdev(pad->entity);
src_fmt.pad = pad->index;
src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt);
if (ret < 0 && ret != -ENOIOCTLCMD)
return -EPIPE;
if (src_fmt.format.width != sink_fmt.format.width ||
src_fmt.format.height != sink_fmt.format.height ||
src_fmt.format.code != sink_fmt.format.code)
return -EPIPE;
}
return 0;
}
static int isp_video_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct fimc_isp *isp = video_drvdata(file);
struct exynos_video_entity *ve = &isp->video_capture.ve;
struct media_entity *me = &ve->vdev.entity;
int ret;
ret = media_pipeline_start(me, &ve->pipe->mp);
if (ret < 0)
return ret;
ret = isp_video_pipeline_validate(isp);
if (ret < 0)
goto p_stop;
ret = vb2_ioctl_streamon(file, priv, type);
if (ret < 0)
goto p_stop;
isp->video_capture.streaming = 1;
return 0;
p_stop:
media_pipeline_stop(me);
return ret;
}
static int isp_video_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
struct fimc_isp *isp = video_drvdata(file);
struct fimc_is_video *video = &isp->video_capture;
int ret;
ret = vb2_ioctl_streamoff(file, priv, type);
if (ret < 0)
return ret;
media_pipeline_stop(&video->ve.vdev.entity);
video->streaming = 0;
return 0;
}
static int isp_video_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *rb)
{
struct fimc_isp *isp = video_drvdata(file);
int ret;
ret = vb2_ioctl_reqbufs(file, priv, rb);
if (ret < 0)
return ret;
if (rb->count && rb->count < FIMC_ISP_REQ_BUFS_MIN) {
rb->count = 0;
vb2_ioctl_reqbufs(file, priv, rb);
ret = -ENOMEM;
}
isp->video_capture.reqbufs_count = rb->count;
return ret;
}
static const struct v4l2_ioctl_ops isp_video_ioctl_ops = {
.vidioc_querycap = isp_video_querycap,
.vidioc_enum_fmt_vid_cap_mplane = isp_video_enum_fmt_mplane,
.vidioc_try_fmt_vid_cap_mplane = isp_video_try_fmt_mplane,
.vidioc_s_fmt_vid_cap_mplane = isp_video_s_fmt_mplane,
.vidioc_g_fmt_vid_cap_mplane = isp_video_g_fmt_mplane,
.vidioc_reqbufs = isp_video_reqbufs,
.vidioc_querybuf = vb2_ioctl_querybuf,
.vidioc_prepare_buf = vb2_ioctl_prepare_buf,
.vidioc_create_bufs = vb2_ioctl_create_bufs,
.vidioc_qbuf = vb2_ioctl_qbuf,
.vidioc_dqbuf = vb2_ioctl_dqbuf,
.vidioc_streamon = isp_video_streamon,
.vidioc_streamoff = isp_video_streamoff,
};
int fimc_isp_video_device_register(struct fimc_isp *isp,
struct v4l2_device *v4l2_dev,
enum v4l2_buf_type type)
{
struct vb2_queue *q = &isp->video_capture.vb_queue;
struct fimc_is_video *iv;
struct video_device *vdev;
int ret;
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
iv = &isp->video_capture;
else
return -ENOSYS;
mutex_init(&isp->video_lock);
INIT_LIST_HEAD(&iv->pending_buf_q);
INIT_LIST_HEAD(&iv->active_buf_q);
iv->format = fimc_isp_find_format(NULL, NULL, 0);
iv->pixfmt.width = IS_DEFAULT_WIDTH;
iv->pixfmt.height = IS_DEFAULT_HEIGHT;
iv->pixfmt.pixelformat = iv->format->fourcc;
iv->pixfmt.colorspace = V4L2_COLORSPACE_SRGB;
iv->reqbufs_count = 0;
memset(q, 0, sizeof(*q));
q->type = type;
q->io_modes = VB2_MMAP | VB2_USERPTR;
q->ops = &isp_video_capture_qops;
q->mem_ops = &vb2_dma_contig_memops;
q->buf_struct_size = sizeof(struct isp_video_buf);
q->drv_priv = isp;
q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->lock = &isp->video_lock;
q->dev = &isp->pdev->dev;
ret = vb2_queue_init(q);
if (ret < 0)
return ret;
vdev = &iv->ve.vdev;
memset(vdev, 0, sizeof(*vdev));
snprintf(vdev->name, sizeof(vdev->name), "fimc-is-isp.%s",
type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ?
"capture" : "output");
vdev->queue = q;
vdev->fops = &isp_video_fops;
vdev->ioctl_ops = &isp_video_ioctl_ops;
vdev->v4l2_dev = v4l2_dev;
vdev->minor = -1;
vdev->release = video_device_release_empty;
vdev->lock = &isp->video_lock;
iv->pad.flags = MEDIA_PAD_FL_SINK;
ret = media_entity_pads_init(&vdev->entity, 1, &iv->pad);
if (ret < 0)
return ret;
video_set_drvdata(vdev, isp);
ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
if (ret < 0) {
media_entity_cleanup(&vdev->entity);
return ret;
}
v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n",
vdev->name, video_device_node_name(vdev));
return 0;
}
void fimc_isp_video_device_unregister(struct fimc_isp *isp,
enum v4l2_buf_type type)
{
struct exynos_video_entity *ve;
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
ve = &isp->video_capture.ve;
else
return;
mutex_lock(&isp->video_lock);
if (video_is_registered(&ve->vdev)) {
video_unregister_device(&ve->vdev);
media_entity_cleanup(&ve->vdev.entity);
ve->pipe = NULL;
}
mutex_unlock(&isp->video_lock);
}