399 lines
9.1 KiB
C
Executable file
399 lines
9.1 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/module.h>
|
|
#include <linux/types.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/string.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/init.h>
|
|
#include <linux/kmod.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/device.h>
|
|
#include <linux/poll.h>
|
|
#include <asm/uaccess.h>
|
|
|
|
#include "vision-config.h"
|
|
#include "vision-dev.h"
|
|
|
|
static struct vision_device *vision_device[VISION_NUM_DEVICES];
|
|
static DEFINE_MUTEX(visiondev_lock);
|
|
|
|
/*****************************************************************************
|
|
***** wrapper function *****
|
|
*****************************************************************************/
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0)
|
|
static int vision_flush(struct file *file, fl_owner_t id)
|
|
{
|
|
struct vision_device *vdev = vision_devdata(file);
|
|
|
|
if (!vdev->fops->flush)
|
|
return -ENOENT;
|
|
|
|
return vdev->fops->flush(file);
|
|
}
|
|
|
|
static int vision_log_probe_w(struct vision_device *vdev)
|
|
{
|
|
#if IS_ENABLED(CONFIG_EXYNOS_MEMORY_LOGGER)
|
|
return vision_log_probe(vdev);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
#else
|
|
static int vision_flush(struct file *file, fl_owner_t id)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int vision_log_probe_w(__attribute__((unused))struct vision_device *vdev)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
struct vision_device *vision_devdata(struct file *file)
|
|
{
|
|
return vision_device[iminor(file_inode(file))];
|
|
}
|
|
|
|
static int vision_open(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = 0;
|
|
struct vision_device *vdev;
|
|
|
|
mutex_lock(&visiondev_lock);
|
|
|
|
vdev = vision_devdata(file);
|
|
if (!vdev) {
|
|
mutex_unlock(&visiondev_lock);
|
|
vision_err("vision device is NULL\n");
|
|
ret = -ENODEV;
|
|
goto p_err;
|
|
}
|
|
|
|
if (!(vdev->flags & (1 << VISION_DEVICE_TYPE_VERTEX))) {
|
|
mutex_unlock(&visiondev_lock);
|
|
vision_err("[V] vision device is not registered\n");
|
|
ret = -ENODEV;
|
|
goto p_err;
|
|
}
|
|
|
|
get_device(&vdev->dev);
|
|
mutex_unlock(&visiondev_lock);
|
|
|
|
ret = (vdev->fops->open ? vdev->fops->open(file) : -EINVAL);
|
|
if (ret) {
|
|
put_device(&vdev->dev);
|
|
goto p_err;
|
|
}
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static int vision_release(struct inode *inode, struct file *file)
|
|
{
|
|
int ret = 0;
|
|
struct vision_device *vdev = vision_devdata(file);
|
|
|
|
ret = (vdev->fops->release ? vdev->fops->release(file) : -EINVAL);
|
|
put_device(&vdev->dev);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long vision_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret = 0;
|
|
struct vision_device *vdev = vision_devdata(file);
|
|
struct mutex *lock = vdev->lock;
|
|
|
|
if (!vdev->fops->ioctl) {
|
|
vision_err("ioctl is not registered\n");
|
|
ret = -ENOTTY;
|
|
goto p_err;
|
|
}
|
|
|
|
if (lock && mutex_lock_interruptible(lock))
|
|
return -ERESTARTSYS;
|
|
|
|
ret = vdev->fops->ioctl(file, cmd, arg);
|
|
|
|
if (lock)
|
|
mutex_unlock(lock);
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static long vision_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret = 0;
|
|
struct vision_device *vdev = vision_devdata(file);
|
|
struct mutex *lock = vdev->lock;
|
|
|
|
if (!vdev->fops->compat_ioctl) {
|
|
vision_err("compat_ioctl is not registered\n");
|
|
ret = -ENOTTY;
|
|
goto p_err;
|
|
}
|
|
|
|
if (lock && mutex_lock_interruptible(lock))
|
|
return -ERESTARTSYS;
|
|
|
|
ret = vdev->fops->compat_ioctl(file, cmd, arg);
|
|
|
|
if (lock)
|
|
mutex_unlock(lock);
|
|
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
static unsigned int vision_poll(struct file *file, struct poll_table_struct *poll)
|
|
{
|
|
struct vision_device *vdev = vision_devdata(file);
|
|
|
|
if (!vdev->fops->poll)
|
|
return DEFAULT_POLLMASK;
|
|
|
|
return vdev->fops->poll(file, poll);
|
|
}
|
|
|
|
static const struct file_operations vision_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = vision_open,
|
|
.release = vision_release,
|
|
.unlocked_ioctl = vision_ioctl,
|
|
.compat_ioctl = vision_compat_ioctl32,
|
|
.poll = vision_poll,
|
|
.llseek = no_llseek,
|
|
.flush = vision_flush,
|
|
};
|
|
|
|
static ssize_t index_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct vision_device *vdev = container_of(dev, struct vision_device, dev);
|
|
|
|
return sprintf(buf, "%i\n", vdev->index);
|
|
}
|
|
static DEVICE_ATTR_RO(index);
|
|
|
|
static ssize_t debug_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct vision_device *vdev = container_of(dev, struct vision_device, dev);
|
|
|
|
return sprintf(buf, "%i\n", vdev->debug);
|
|
}
|
|
|
|
static ssize_t debug_store(struct device *dev,
|
|
struct device_attribute *attr, const char *buf, size_t len)
|
|
{
|
|
struct vision_device *vdev = container_of(dev, struct vision_device, dev);
|
|
int res = 0;
|
|
u16 value;
|
|
|
|
res = kstrtou16(buf, 0, &value);
|
|
if (res) {
|
|
vision_err("kstrtou16 is fail\n");
|
|
return res;
|
|
}
|
|
|
|
vdev->debug = value;
|
|
return len;
|
|
}
|
|
static DEVICE_ATTR_RW(debug);
|
|
|
|
static ssize_t name_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct vision_device *vdev = container_of(dev, struct vision_device, dev);
|
|
|
|
return sprintf(buf, "%.*s\n", (int)sizeof(vdev->name), vdev->name);
|
|
}
|
|
static DEVICE_ATTR_RO(name);
|
|
|
|
static ssize_t tpf_show(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct vision_device *vdev = container_of(dev, struct vision_device, dev);
|
|
|
|
return sprintf(buf, "%lld\n", vdev->tpf);
|
|
}
|
|
static DEVICE_ATTR_RO(tpf);
|
|
|
|
static struct attribute *vision_device_attrs[] = {
|
|
&dev_attr_name.attr,
|
|
&dev_attr_debug.attr,
|
|
&dev_attr_index.attr,
|
|
&dev_attr_tpf.attr,
|
|
NULL,
|
|
};
|
|
ATTRIBUTE_GROUPS(vision_device);
|
|
|
|
struct class vision_class = {
|
|
.name = VISION_NAME,
|
|
.dev_groups = vision_device_groups,
|
|
};
|
|
|
|
static void vision_device_release(struct device *dev)
|
|
{
|
|
struct vision_device *vdev = container_of(dev, struct vision_device, dev);
|
|
|
|
mutex_lock(&visiondev_lock);
|
|
if (WARN_ON(vision_device[vdev->minor] != vdev)) {
|
|
/* should not happen */
|
|
mutex_unlock(&visiondev_lock);
|
|
return;
|
|
}
|
|
|
|
/* Free up this device for reuse */
|
|
vision_device[vdev->minor] = NULL;
|
|
|
|
/* Delete the cdev on this minor as well */
|
|
cdev_del(vdev->cdev);
|
|
/* Just in case some driver tries to access this from
|
|
* the release() callback.
|
|
*/
|
|
vdev->cdev = NULL;
|
|
|
|
mutex_unlock(&visiondev_lock);
|
|
|
|
/* Release video_device and perform other
|
|
* cleanups as needed.
|
|
*/
|
|
vdev->release(vdev);
|
|
}
|
|
|
|
int vision_register_device(struct vision_device *vdev, int minor, struct module *owner)
|
|
{
|
|
int ret = 0;
|
|
const char *name_base;
|
|
|
|
BUG_ON(!vdev->parent);
|
|
WARN_ON(vision_device[minor] != NULL);
|
|
|
|
vdev->cdev = NULL;
|
|
|
|
switch (vdev->type) {
|
|
case VISION_DEVICE_TYPE_VERTEX:
|
|
name_base = "vertex";
|
|
break;
|
|
default:
|
|
vprobe_err("%s called with unknown type: %d\n", __func__, vdev->type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Part 3: Initialize the character device */
|
|
vdev->cdev = cdev_alloc();
|
|
if (vdev->cdev == NULL) {
|
|
ret = -ENOMEM;
|
|
goto cleanup;
|
|
}
|
|
vdev->cdev->ops = &vision_fops;
|
|
vdev->cdev->owner = owner;
|
|
ret = cdev_add(vdev->cdev, MKDEV(VISION_MAJOR, minor), 1);
|
|
if (ret < 0) {
|
|
vprobe_err("%s: cdev_add failed\n", __func__);
|
|
kfree(vdev->cdev);
|
|
vdev->cdev = NULL;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Part 4: register the device with sysfs */
|
|
vdev->dev.class = &vision_class;
|
|
vdev->dev.parent = vdev->parent;
|
|
vdev->dev.devt = MKDEV(VISION_MAJOR, minor);
|
|
dev_set_name(&vdev->dev, "%s%d", name_base, minor);
|
|
ret = device_register(&vdev->dev);
|
|
if (ret < 0) {
|
|
vprobe_err("%s: device_register failed\n", __func__);
|
|
goto cleanup;
|
|
}
|
|
/* Register the release callback that will be called when the last
|
|
* reference to the device goes away.
|
|
*/
|
|
vdev->dev.release = vision_device_release;
|
|
|
|
ret = vision_log_probe_w(vdev);
|
|
if (ret) {
|
|
vprobe_err("%s: vsion_log_probe failed\n", __func__);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Part 6: Activate this minor. The char device can now be used. */
|
|
set_bit(VISION_FL_REGISTERED, &vdev->flags);
|
|
|
|
mutex_lock(&visiondev_lock);
|
|
vdev->minor = minor;
|
|
vision_device[minor] = vdev;
|
|
mutex_unlock(&visiondev_lock);
|
|
|
|
return 0;
|
|
|
|
cleanup:
|
|
mutex_lock(&visiondev_lock);
|
|
if (vdev->cdev)
|
|
cdev_del(vdev->cdev);
|
|
/* Mark this video device as never having been registered. */
|
|
vdev->minor = -1;
|
|
mutex_unlock(&visiondev_lock);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(vision_register_device);
|
|
|
|
//#ifdef CONFIG_NPU_BUILD_BUILTIN
|
|
//static int __init visiondev_init(void)
|
|
//{
|
|
// int ret;
|
|
// dev_t dev = MKDEV(VISION_MAJOR, 0);
|
|
//
|
|
// vision_info("Linux vision interface: v1.00\n");
|
|
// ret = register_chrdev_region(dev, VISION_NUM_DEVICES, VISION_NAME);
|
|
// if (ret < 0) {
|
|
// vision_err("videodev: unable to get major %d\n", VISION_MAJOR);
|
|
// return ret;
|
|
// }
|
|
//
|
|
// ret = class_register(&vision_class);
|
|
// if (ret < 0) {
|
|
// unregister_chrdev_region(dev, VISION_NUM_DEVICES);
|
|
// vision_err("video_dev: class_register failed\n");
|
|
// return -EIO;
|
|
// }
|
|
//
|
|
// return 0;
|
|
//}
|
|
//
|
|
//static void __exit visiondev_exit(void)
|
|
//{
|
|
// dev_t dev = MKDEV(VISION_MAJOR, 0);
|
|
//
|
|
// class_unregister(&vision_class);
|
|
// unregister_chrdev_region(dev, VISION_NUM_DEVICES);
|
|
//}
|
|
//
|
|
//subsys_initcall(visiondev_init);
|
|
//module_exit(visiondev_exit)
|
|
//
|
|
//MODULE_AUTHOR("Gilyeon Lim <kilyeon.im@samsung.com>");
|
|
//MODULE_DESCRIPTION("Device registrar for Vision4Linux drivers v1");
|
|
//MODULE_LICENSE("GPL");
|
|
//MODULE_ALIAS_CHARDEV_MAJOR(VISION_MAJOR);
|
|
//#endif
|