blob: 52ed22210d3a0ab45dac816c024b0ad36e522cee [file] [log] [blame]
/*
* 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/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <video/videonode.h>
#include <media/exynos_mc.h>
#include <asm/cacheflush.h>
#include <asm/pgtable.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/bug.h>
#include "vpu-config.h"
#include "vision-ioctl.h"
#include "vpu-vertex.h"
#include "vision-dev.h"
#include "vpu-device.h"
#include "vpu-graphmgr.h"
#include "vpu-graph.h"
#include "vpu-queue.h"
#include "vpu-control.h"
const struct vision_file_ops vpu_vertex_fops;
const const struct vertex_ioctl_ops vpu_vertex_ioctl_ops;
static int __vref_open(struct vpu_vertex *vertex)
{
struct vpu_device *device = container_of(vertex, struct vpu_device, vertex);
atomic_set(&vertex->start_cnt.refcount, 0);
return vpu_device_open(device);
}
static int __vref_close(struct vpu_vertex *vertex)
{
struct vpu_device *device = container_of(vertex, struct vpu_device, vertex);
return vpu_device_close(device);
}
static int __vref_start(struct vpu_vertex *vertex)
{
struct vpu_device *device = container_of(vertex, struct vpu_device, vertex);
return vpu_device_start(device);
}
static int __vref_stop(struct vpu_vertex *vertex)
{
struct vpu_device *device = container_of(vertex, struct vpu_device, vertex);
return vpu_device_stop(device);
}
static inline void __vref_init(struct vpu_vertex_refcount *vref,
struct vpu_vertex *vertex, int (*first)(struct vpu_vertex *vertex), int (*final)(struct vpu_vertex *vertex))
{
vref->vertex = vertex;
vref->first = first;
vref->final = final;
atomic_set(&vref->refcount, 0);
}
static inline int __vref_get(struct vpu_vertex_refcount *vref)
{
return (atomic_inc_return(&vref->refcount) == 1) ? vref->first(vref->vertex) : 0;
}
static inline int __vref_put(struct vpu_vertex_refcount *vref)
{
return (atomic_dec_return(&vref->refcount) == 0) ? vref->final(vref->vertex) : 0;
}
int vpu_vertex_probe(struct vpu_vertex *vertex, struct device *parent)
{
int ret = 0;
struct vision_device *vdev;
BUG_ON(!vertex);
BUG_ON(!parent);
get_device(parent);
mutex_init(&vertex->lock);
__vref_init(&vertex->open_cnt, vertex, __vref_open, __vref_close);
__vref_init(&vertex->start_cnt, vertex, __vref_start, __vref_stop);
vdev = &vertex->vd;
snprintf(vdev->name, sizeof(vdev->name), "%s", VPU_VERTEX_NAME);
vdev->fops = &vpu_vertex_fops;
vdev->ioctl_ops = &vpu_vertex_ioctl_ops;
vdev->release = NULL;
vdev->lock = NULL;
vdev->parent = parent;
vdev->type = VISION_DEVICE_TYPE_VERTEX;
dev_set_drvdata(&vdev->dev, vertex);
ret = vision_register_device(vdev, VPU_VERTEX_MINOR, vpu_vertex_fops.owner);
if (ret) {
probe_err("vision_register_device is fail(%d)\n", ret);
goto p_err;
}
probe_info("%s():%d\n", __func__, ret);
p_err:
return ret;
}
/*
* =============================================================================
* Video File Opertation
* =============================================================================
*/
static int vpu_vertex_open(struct file *file)
{
int ret = 0;
struct vpu_vertex *vertex = dev_get_drvdata(&vision_devdata(file)->dev);
struct vpu_device *device = container_of(vertex, struct vpu_device, vertex);
struct mutex *lock = &vertex->lock;
struct vpu_vertex_ctx *vctx;
struct vpu_graph *graph;
if (mutex_lock_interruptible(lock)) {
vpu_err("mutex_lock_interruptible is fail\n");
return -ERESTARTSYS;
}
ret = __vref_get(&vertex->open_cnt);
if (ret) {
vpu_err("vref_get is fail(%d)", ret);
goto p_err;
}
ret = vpu_graph_create(&graph,
&device->graphmgr,
&device->resource,
&device->system.memory,
&device->system.exynos);
if (ret) {
vpu_err("vpu_graph_create is fail(%d)", ret);
goto p_err;
}
vctx = &graph->vctx;
vctx->id = graph->id;
vctx->vertex = vertex;
mutex_init(&vctx->lock);
ret = vpu_queue_open(&vctx->queue, &device->system.memory, &vctx->lock);
if (ret) {
vpu_err("vpu_queue_open is fail(%d)", ret);
goto p_err;
}
file->private_data = vctx;
vctx->state = BIT(VPU_VERTEX_OPEN);
p_err:
vpu_iinfo("%s():%d\n", vctx, __func__, ret);
mutex_unlock(lock);
return ret;
}
static int vpu_vertex_close(struct file *file)
{
int ret = 0;
struct vpu_vertex_ctx *vctx = file->private_data;
struct vpu_graph *graph = container_of(vctx, struct vpu_graph, vctx);
struct vpu_vertex *vertex = vctx->vertex;
struct mutex *lock = &vertex->lock;
int id = vctx->id;
if (mutex_lock_interruptible(lock)) {
vpu_ierr("mutex_lock_interruptible is fail\n", vctx);
return -ERESTARTSYS;
}
ret = vpu_graph_destroy(graph);
if (ret) {
vpu_err("vpu_graph_destroy is fail(%d)", ret);
goto p_err;
}
ret = __vref_put(&vertex->open_cnt);
if (ret) {
vpu_err("vref_put is fail(%d)", ret);
goto p_err;
}
p_err:
vpu_info("[I%d]%s():%d\n", id, __func__, ret);
mutex_unlock(lock);
return ret;
}
static unsigned int vpu_vertex_poll(struct file *file,
poll_table *poll)
{
int ret = 0;
struct vpu_vertex_ctx *vctx = file->private_data;
struct vpu_queue *queue = &vctx->queue;
if (!(vctx->state & BIT(VPU_VERTEX_START))) {
vpu_ierr("invalid state(%X)", vctx, vctx->state);
ret |= POLLERR;
goto p_err;
}
ret = vpu_queue_poll(queue, file, poll);
p_err:
return ret;
}
const struct vision_file_ops vpu_vertex_fops = {
.owner = THIS_MODULE,
.open = vpu_vertex_open,
.release = vpu_vertex_close,
.poll = vpu_vertex_poll,
.ioctl = vertex_ioctl,
.compat_ioctl = vertex_compat_ioctl32
};
/*
* =============================================================================
* Video Ioctl Opertation
* =============================================================================
*/
static int vpu_vertex_s_graph(struct file *file, struct vs4l_graph *ginfo)
{
int ret = 0;
struct vpu_vertex_ctx *vctx = file->private_data;
struct vpu_graph *graph = container_of(vctx, struct vpu_graph, vctx);
struct mutex *lock = &vctx->lock;
if (mutex_lock_interruptible(lock)) {
vpu_ierr("mutex_lock_interruptible is fail\n", vctx);
return -ERESTARTSYS;
}
if (!(vctx->state & BIT(VPU_VERTEX_OPEN))) {
vpu_ierr("invalid state(%X)\n", vctx, vctx->state);
ret = -EINVAL;
goto p_err;
}
ret = vpu_graph_config(graph, ginfo);
if (ret) {
vpu_err("vpu_graph_config is fail(%d)\n", ret);
goto p_err;
}
vctx->state = BIT(VPU_VERTEX_GRAPH);
p_err:
mutex_unlock(lock);
return ret;
}
static int vpu_vertex_s_format(struct file *file, struct vs4l_format_list *flist)
{
int ret = 0;
struct vpu_vertex_ctx *vctx = file->private_data;
struct vpu_queue *queue = &vctx->queue;
struct mutex *lock = &vctx->lock;
if (mutex_lock_interruptible(lock)) {
vpu_ierr("mutex_lock_interruptible is fail\n", vctx);
return -ERESTARTSYS;
}
if (!(vctx->state & (BIT(VPU_VERTEX_GRAPH) | BIT(VPU_VERTEX_FORMAT)))) {
vpu_ierr("invalid state(%X)\n", vctx, vctx->state);
ret = -EINVAL;
goto p_err;
}
ret = vpu_queue_s_format(queue, flist);
if (ret) {
vpu_ierr("vpu_queue_s_format is fail(%d)\n", vctx, ret);
goto p_err;
}
vctx->state = BIT(VPU_VERTEX_FORMAT);
p_err:
vpu_iinfo("%s():%d\n", vctx, __func__, ret);
mutex_unlock(lock);
return ret;
}
static int vpu_vertex_s_param(struct file *file, struct vs4l_param_list *plist)
{
int ret = 0;
struct vpu_vertex_ctx *vctx = file->private_data;
struct vpu_graph *graph = container_of(vctx, struct vpu_graph, vctx);
struct mutex *lock = &vctx->lock;
if (mutex_lock_interruptible(lock)) {
vpu_ierr("mutex_lock_interruptible is fail\n", vctx);
return -ERESTARTSYS;
}
if (!(vctx->state & BIT(VPU_VERTEX_START))) {
vpu_ierr("invalid state(%X)\n", vctx, vctx->state);
ret = -EINVAL;
goto p_err;
}
ret = vpu_graph_param(graph, plist);
if (ret) {
vpu_err("vpu_graph_param is fail(%d)\n", ret);
goto p_err;
}
p_err:
vpu_iinfo("%s():%d\n", vctx, __func__, ret);
mutex_unlock(lock);
return ret;
}
static int vpu_vertex_s_ctrl(struct file *file, struct vs4l_ctrl *ctrl)
{
int ret = 0;
struct vpu_vertex_ctx *vctx = file->private_data;
struct vpu_vertex *vertex = dev_get_drvdata(&vision_devdata(file)->dev);
struct vpu_device *device = container_of(vertex, struct vpu_device, vertex);
struct vpu_graph *graph = container_of(vctx, struct vpu_graph, vctx);
struct mutex *lock = &vctx->lock;
if (mutex_lock_interruptible(lock)) {
vpu_ierr("mutex_lock_interruptible is fail\n", vctx);
return -ERESTARTSYS;
}
switch (ctrl->ctrl) {
case VPU_CTRL_DUMP:
vpu_graph_print(graph);
break;
case VPU_CTRL_MODE:
device->mode = ctrl->value;
break;
case VPU_CTRL_TEST:
if (ctrl->value == 1) {
ret = vpu_tv_do_depth3(&device->system.tvset);
if (ret) {
vpu_err("vpu_tv_do_depth3 is fail(%d)\n", ret);
break;
}
} else {
ret = vpu_tv_do_flamorb(&device->system.tvset);
if (ret) {
vpu_err("vpu_tv_do_flamorb is fail(%d)\n", ret);
break;
}
}
break;
default:
vpu_ierr("request control is invalid(%d)\n", vctx, ctrl->ctrl);
ret = -EINVAL;
break;
}
vpu_iinfo("%s(%d):%d\n", vctx, __func__, ctrl->ctrl, ret);
mutex_unlock(lock);
return ret;
}
static int vpu_vertex_qbuf(struct file *file, struct vs4l_container_list *clist)
{
int ret = 0;
struct vpu_vertex_ctx *vctx = file->private_data;
struct vpu_queue *queue = &vctx->queue;
struct mutex *lock = &vctx->lock;
if (mutex_lock_interruptible(lock)) {
vpu_ierr("mutex_lock_interruptible is fail\n", vctx);
return -ERESTARTSYS;
}
if (!(vctx->state & BIT(VPU_VERTEX_START))) {
vpu_ierr("(%d) invalid state(%X)\n", vctx, clist->direction, vctx->state);
ret = -EINVAL;
goto p_err;
}
ret = vpu_queue_qbuf(queue, clist);
if (ret) {
vpu_ierr("(%d) vpu_queue_qbuf is fail(%d)\n", vctx, clist->direction, ret);
goto p_err;
}
p_err:
mutex_unlock(lock);
return ret;
}
static int vpu_vertex_dqbuf(struct file *file, struct vs4l_container_list *clist)
{
int ret = 0;
struct vpu_vertex_ctx *vctx = file->private_data;
struct vpu_queue *queue = &vctx->queue;
struct mutex *lock = &vctx->lock;
bool nonblocking = file->f_flags & O_NONBLOCK;
if (mutex_lock_interruptible(lock)) {
vpu_ierr("mutex_lock_interruptible is fail\n", vctx);
return -ERESTARTSYS;
}
if (!(vctx->state & BIT(VPU_VERTEX_START))) {
vpu_ierr("(%d) invalid state(%X)\n", vctx, clist->direction, vctx->state);
ret = -EINVAL;
goto p_err;
}
ret = vpu_queue_dqbuf(queue, clist, nonblocking);
if (ret) {
vpu_ierr("(%d) vpu_queue_dqbuf is fail(%d)\n", vctx, clist->direction, ret);
goto p_err;
}
p_err:
mutex_unlock(lock);
return ret;
}
static int vpu_vertex_streamon(struct file *file)
{
int ret = 0;
struct vpu_vertex_ctx *vctx = file->private_data;
struct vpu_vertex *vertex = vctx->vertex;
struct vpu_queue *queue = &vctx->queue;
struct mutex *lock = &vctx->lock;
if (mutex_lock_interruptible(lock)) {
vpu_ierr("mutex_lock_interruptible is fail\n", vctx);
return -ERESTARTSYS;
}
if (!(vctx->state & (BIT(VPU_VERTEX_FORMAT) | BIT(VPU_VERTEX_STOP)))) {
vpu_ierr("invalid state(%X)\n", vctx, vctx->state);
ret = -EINVAL;
goto p_err;
}
ret = __vref_get(&vertex->start_cnt);
if (ret) {
vpu_err("vref_get is fail(%d)\n", ret);
goto p_err;
}
ret = vpu_queue_start(queue);
if (ret) {
vpu_ierr("vpu_queue_start is fail(%d)\n", vctx, ret);
goto p_err;
}
vctx->state = BIT(VPU_VERTEX_START);
p_err:
vpu_iinfo("%s():%d\n", vctx, __func__, ret);
mutex_unlock(lock);
return ret;
}
static int vpu_vertex_streamoff(struct file *file)
{
int ret = 0;
struct vpu_vertex_ctx *vctx = file->private_data;
struct vpu_vertex *vertex = vctx->vertex;
struct vpu_queue *queue = &vctx->queue;
struct mutex *lock = &vctx->lock;
if (mutex_lock_interruptible(lock)) {
vpu_ierr("mutex_lock_interruptible is fail\n", vctx);
return -ERESTARTSYS;
}
if (!(vctx->state & BIT(VPU_VERTEX_START))) {
vpu_ierr("invalid state(%X)\n", vctx, vctx->state);
ret = -EINVAL;
goto p_err;
}
ret = vpu_queue_stop(queue);
if (ret) {
vpu_ierr("vpu_queue_stop is fail(%d)\n", vctx, ret);
goto p_err;
}
ret = __vref_put(&vertex->start_cnt);
if (ret) {
vpu_err("vref_put is fail(%d)\n", ret);
goto p_err;
}
vctx->state = BIT(VPU_VERTEX_STOP);
p_err:
vpu_iinfo("%s():%d\n", vctx, __func__, ret);
mutex_unlock(lock);
return ret;
}
const struct vertex_ioctl_ops vpu_vertex_ioctl_ops = {
.vertexioc_s_graph = vpu_vertex_s_graph,
.vertexioc_s_format = vpu_vertex_s_format,
.vertexioc_s_param = vpu_vertex_s_param,
.vertexioc_s_ctrl = vpu_vertex_s_ctrl,
.vertexioc_qbuf = vpu_vertex_qbuf,
.vertexioc_dqbuf = vpu_vertex_dqbuf,
.vertexioc_streamon = vpu_vertex_streamon,
.vertexioc_streamoff = vpu_vertex_streamoff
};