mirror of
https://github.com/thead-yocto-mirror/vi-kernel
synced 2026-07-15 20:45:48 +02:00
456 lines
12 KiB
C
Executable File
456 lines
12 KiB
C
Executable File
/*
|
|
* Copyright (C) 2021 Alibaba Group Holding Limited
|
|
* Author: liuyitong <yitong.lyt@alibaba-inc.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 <asm/io.h>
|
|
|
|
#include <linux/cdev.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/clk.h>
|
|
#include <linux/io.h>
|
|
#include <linux/mm.h>
|
|
|
|
#include <linux/timer.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/ioctl.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/debugfs.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/of.h>
|
|
#include "video.h"
|
|
#include "video_ioctl.h"
|
|
|
|
#define VIDEO_DRIVER_NAME "video"
|
|
#define VIDEO_DRIVER_MAXCNT 100
|
|
|
|
|
|
static struct class *video_driver_class;
|
|
static unsigned int video_driver_major = 0;
|
|
static unsigned int video_driver_minor = 0;
|
|
static unsigned int device_register_index = 0;
|
|
|
|
#define check_retval(x)\
|
|
do {\
|
|
if ((x))\
|
|
return -EIO;\
|
|
} while (0)
|
|
|
|
static unsigned int video_poll(struct file * filp, poll_table *wait)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void video_work(struct work_struct *work)
|
|
{
|
|
|
|
}
|
|
|
|
irqreturn_t video_irq(int irq, void *dev_id)
|
|
{
|
|
video_info("enter %s\n", __func__);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int video_open(struct inode * inode, struct file * file)
|
|
{
|
|
struct video_drvdata *drvdata;
|
|
drvdata = container_of(inode->i_cdev, struct video_drvdata, cdev);
|
|
file->private_data = drvdata;
|
|
|
|
return 0;
|
|
};
|
|
|
|
static int video_get_pipelines(struct video_drvdata *drvdata, void *__user args)
|
|
{
|
|
pipline_t *usr_pip = (pipline_t *)args;
|
|
pipline_t *cam_pip = drvdata->piplines;
|
|
|
|
check_retval(copy_to_user(usr_pip, cam_pip, sizeof(pipline_t) * drvdata->pipline_num));
|
|
video_info("%s, %d, pipeline nmu: %d\n", __func__, __LINE__, drvdata->pipline_num);
|
|
return 0;
|
|
}
|
|
|
|
static int video_get_pipelines_num (struct video_drvdata *drvdata, void *__user args)
|
|
{
|
|
check_retval(copy_to_user(args, &drvdata->pipline_num, sizeof(int)));
|
|
return 0;
|
|
}
|
|
|
|
static int video_set_path_type(struct video_drvdata *drvdata, void *__user args)
|
|
{
|
|
pathtype_set_t pathtype_set;
|
|
check_retval(copy_from_user(&pathtype_set, args, sizeof(pathtype_set_t)));
|
|
video_info("VIDEO_SET_PATH_TYPE pipeline=%d sensor=%s path_type%d \n", pathtype_set.pipeline_id , pathtype_set.sensor_name, pathtype_set.path_type);
|
|
return 0;
|
|
}
|
|
|
|
static int video_get_mem_pool_region_id(struct video_drvdata *drvdata, void *__user args)
|
|
{
|
|
check_retval(copy_to_user(args, &drvdata->vi_mem_pool_region_id, sizeof(int)));
|
|
return 0;
|
|
}
|
|
static long video_set_state(struct video_drvdata *drvdata,int *arg)
|
|
{
|
|
int ops;
|
|
check_retval(copy_from_user(&ops,arg,sizeof(int)));
|
|
if(ops ==DEVICE_OPERATION_ACTIVE)
|
|
{
|
|
if(drvdata->actived == true)
|
|
{
|
|
dev_warn(drvdata->dev,"%s,can't ative,device already active by other process(%d)\n",__func__, current->tgid);
|
|
return (-EBUSY);
|
|
}else
|
|
{
|
|
drvdata->actived =true;
|
|
drvdata->actived_owner = current->tgid;
|
|
dev_dbg(drvdata->dev,"%s,actived by process %d\n",__func__,current->tgid);
|
|
}
|
|
}
|
|
else if(ops ==DEVICE_OPERATION_DEACTIVE)
|
|
{
|
|
if(drvdata->actived == false)
|
|
{
|
|
dev_warn(drvdata->dev,"%s,can't deative,device is not atived\n",__func__);
|
|
return (-EPERM);
|
|
}else
|
|
{
|
|
if(drvdata->actived_owner != current->tgid)
|
|
{
|
|
dev_warn(drvdata->dev,"%s,can't deative,device is active by other process(%d)\n",__func__, current->tgid);
|
|
return (-EPERM);
|
|
}
|
|
drvdata->actived =false;
|
|
drvdata->actived_owner = -1;
|
|
dev_dbg(drvdata->dev,"%s de-active",__func__);
|
|
}
|
|
}else
|
|
{
|
|
dev_warn(drvdata->dev,"%s,un-defined ops\n",__func__);
|
|
return (-EPERM);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static long video_get_state(struct video_drvdata *drvdata,int *arg)
|
|
{
|
|
int ops;
|
|
ops = drvdata->actived==true?DEVICE_OPERATION_ACTIVE:DEVICE_OPERATION_DEACTIVE;
|
|
check_retval(copy_to_user(arg, &ops, sizeof(int)));
|
|
return 0;
|
|
}
|
|
static long video_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
long ret = 0;
|
|
struct video_drvdata *drvdata;
|
|
|
|
drvdata = file->private_data;
|
|
if (drvdata == NULL) {
|
|
video_err("%s:file private is null point error\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
mutex_lock(&drvdata->mutex);
|
|
switch (cmd) {
|
|
case VIDEO_GET_PIPLANES:
|
|
ret = video_get_pipelines(drvdata, (void *)arg);
|
|
break;
|
|
case VIDEO_GET_PIPLANES_NUM:
|
|
ret = video_get_pipelines_num(drvdata, (void *)arg);
|
|
break;
|
|
case VIDEO_SET_PATH_TYPE:
|
|
ret = video_set_path_type(drvdata, (void *)arg);
|
|
break;
|
|
case VIDEO_GET_MEM_POOL_REGION_ID:
|
|
ret = video_get_mem_pool_region_id(drvdata, (void *)arg);
|
|
break;
|
|
case VIDEO_SET_DEVICE_STATE:
|
|
ret = video_set_state(drvdata, (void *)arg);
|
|
break;
|
|
case VIDEO_GET_DEVICE_STATE:
|
|
ret = video_get_state(drvdata, (void *)arg);
|
|
break;
|
|
default:
|
|
ret = -EPERM;
|
|
video_err("%s: unsupported command %d\n", __func__, cmd);
|
|
break;
|
|
}
|
|
mutex_unlock(&drvdata->mutex);
|
|
return ret;
|
|
};
|
|
|
|
static int video_release(struct inode * inode, struct file * file)
|
|
{
|
|
struct video_drvdata *drvdata;
|
|
drvdata = container_of(inode->i_cdev, struct video_drvdata, cdev);
|
|
mutex_lock(&drvdata->mutex);
|
|
if(drvdata->actived == true &&
|
|
drvdata->actived_owner == current->tgid)
|
|
{
|
|
drvdata->actived = false;
|
|
drvdata->actived_owner = -1;
|
|
dev_dbg(drvdata->dev,"%s de-active device\n",__func__);
|
|
}
|
|
mutex_unlock(&drvdata->mutex);
|
|
|
|
return 0;
|
|
};
|
|
|
|
static int video_mmap(struct file *pFile, struct vm_area_struct *vma)
|
|
{
|
|
video_info("enter %s\n", __func__);
|
|
return 0;
|
|
};
|
|
|
|
struct file_operations video_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = video_open,
|
|
.release = video_release,
|
|
.unlocked_ioctl = video_ioctl,
|
|
.mmap = video_mmap,
|
|
.poll = video_poll,
|
|
};
|
|
|
|
static int video_press_pipline(pipline_t *pipline, struct fwnode_handle *pip_node)
|
|
{
|
|
int ret = 0;
|
|
const char *dev_name;
|
|
const char *path_name;
|
|
struct fwnode_handle *nodes;
|
|
sub_dev_info_t *sub_dev = pipline->sub_dev;
|
|
|
|
|
|
fwnode_for_each_available_child_node(pip_node, nodes) {
|
|
ret = fwnode_property_read_string(nodes, "subdev_name", &dev_name);
|
|
if (ret != 0) {
|
|
video_err("dts press error!!!!!, %s, %d\n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
strcpy(sub_dev->name, dev_name);
|
|
|
|
ret = fwnode_property_read_u32_array(nodes, "idx", &sub_dev->idx, 1);
|
|
if (ret != 0) {
|
|
video_err("dts press device :%s error!!!!!, %d, %s\n", __func__, __LINE__,dev_name);
|
|
return -1;
|
|
}
|
|
|
|
ret = fwnode_property_read_string(nodes, "path_type", &path_name);
|
|
if (ret != 0) {
|
|
video_err("%s, %d, dts parse %s->%s error!!!!!\n", __func__, __LINE__, dev_name, path_name);
|
|
return -1;
|
|
}
|
|
|
|
video_info("%s, %d, dev: %s->%s !!!!!\n", __func__, __LINE__, dev_name, path_name);
|
|
sub_dev->path = vedio_get_path_type(dev_name, path_name);
|
|
if(sub_dev->path < 0) {
|
|
video_err("%s, %d, %s, parse path(%s) error!!!!!\n", __func__, __LINE__, dev_name, path_name);
|
|
return -1;
|
|
}
|
|
video_info("subdev:%s-%d,path:(%s,%d)\n",dev_name,sub_dev->idx, path_name,sub_dev->path);
|
|
|
|
ret = video_subdev_press_dts(nodes, sub_dev);
|
|
if (ret != 0) {
|
|
video_err("dts press error!!!!!, %s, %d\n", __func__, __LINE__);
|
|
return -1;
|
|
}
|
|
|
|
sub_dev++;
|
|
pipline->sub_dev_num++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int video_press_dts(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
struct device *dev = &pdev->dev;
|
|
struct fwnode_handle *child;
|
|
struct video_drvdata *drvdata;
|
|
pipline_t *pipline;
|
|
int pool_region_id;
|
|
|
|
drvdata = platform_get_drvdata(pdev);
|
|
pipline = drvdata->piplines;
|
|
|
|
//if (device_property_read_u32(&(pdev->dev), "vi_mem_pool_region", &pool_region_id) != 0) {
|
|
if (of_property_read_s32(dev->of_node, "vi_mem_pool_region", &pool_region_id) != 0) {
|
|
video_warn("%s, dev(%s) can't get vi_mem_pool_region id from dts, set to default -1(cma)\n",
|
|
__func__, pdev->name);
|
|
pool_region_id = -1;
|
|
}
|
|
drvdata->vi_mem_pool_region_id = pool_region_id;
|
|
|
|
device_for_each_child_node(dev, child) {
|
|
video_info("pipleline(%d):",drvdata->pipline_num);
|
|
ret = video_press_pipline(pipline, child);
|
|
if (ret != 0) {
|
|
video_err("%s, %d, dts press pipe:%d error!\n", __func__, __LINE__, drvdata->pipline_num);
|
|
return -1;
|
|
}
|
|
pipline++;
|
|
drvdata->pipline_num++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int video_probe(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
struct video_drvdata *drvdata;
|
|
struct device_node *np = pdev->dev.of_node;
|
|
|
|
video_info("enter %s\n", __func__);
|
|
pdev->id = of_alias_get_id(np, "viv_video");
|
|
|
|
video_info("%s:pdev id is %d\n", __func__, pdev->id);
|
|
|
|
if (pdev->id >= VIDEO_DRIVER_MAXCNT) {
|
|
video_err("%s:pdev id is %d error\n", __func__, pdev->id);
|
|
return -EINVAL;
|
|
}
|
|
|
|
drvdata = devm_kzalloc(&pdev->dev,sizeof(struct video_drvdata), GFP_KERNEL);
|
|
if (drvdata == NULL) {
|
|
video_err("%s:alloc struct drvdata error\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
drvdata->device_idx = pdev->id;
|
|
drvdata->actived = false;
|
|
drvdata->actived_owner = -1;
|
|
drvdata->dev = &pdev->dev;
|
|
mutex_init(&drvdata->mutex);
|
|
platform_set_drvdata(pdev, drvdata);
|
|
|
|
if (device_register_index == 0) {
|
|
if (video_driver_major == 0) {
|
|
ret = alloc_chrdev_region(&drvdata->devt, 0, VIDEO_DRIVER_MAXCNT, VIDEO_DRIVER_NAME);
|
|
if (ret != 0) {
|
|
video_err("%s:alloc_chrdev_region error\n", __func__);
|
|
return ret;
|
|
}
|
|
video_driver_major = MAJOR(drvdata->devt);
|
|
video_driver_minor = MINOR(drvdata->devt);
|
|
} else {
|
|
drvdata->devt = MKDEV(video_driver_major, video_driver_minor);
|
|
ret = register_chrdev_region(drvdata->devt, VIDEO_DRIVER_MAXCNT, VIDEO_DRIVER_NAME);
|
|
if (ret) {
|
|
video_err("%s:register_chrdev_region error\n", __func__);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
video_driver_class = class_create(THIS_MODULE, VIDEO_DRIVER_NAME);
|
|
if (IS_ERR(video_driver_class)) {
|
|
video_err("%s[%d]:class_create error!\n", __func__, __LINE__);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
drvdata->devt = MKDEV(video_driver_major, video_driver_minor + pdev->id);
|
|
cdev_init(&drvdata->cdev, &video_fops);
|
|
ret = cdev_add(&drvdata->cdev, drvdata->devt, 1);
|
|
if ( ret ) {
|
|
video_err("%s[%d]:cdev_add error!\n", __func__, __LINE__);
|
|
return ret;
|
|
} else {
|
|
video_info("%s[%d]:cdev_add OK, major=%d, minor=%d\n", __func__, __LINE__,
|
|
video_driver_major, video_driver_minor + pdev->id);
|
|
}
|
|
|
|
drvdata->class = video_driver_class;
|
|
device_create(drvdata->class, NULL, drvdata->devt,
|
|
drvdata, "%s%d", VIDEO_DRIVER_NAME, pdev->id);
|
|
|
|
ret = video_press_dts(pdev);
|
|
if (ret) {
|
|
video_err("%s[%d]:dts error!\n", __func__, __LINE__);
|
|
return ret;
|
|
}
|
|
|
|
video_create_capabilities_sysfs(pdev);
|
|
device_register_index++;
|
|
|
|
video_info("exit %s:[%s%d]\n", __func__, VIDEO_DRIVER_NAME, pdev->id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int video_remove(struct platform_device *pdev)
|
|
{
|
|
struct video_drvdata *drvdata;
|
|
|
|
video_info("enter %s\n", __func__);
|
|
device_register_index--;
|
|
drvdata = platform_get_drvdata(pdev);
|
|
cdev_del(&drvdata->cdev);
|
|
device_destroy(drvdata->class, drvdata->devt);
|
|
unregister_chrdev_region(drvdata->devt, VIDEO_DRIVER_MAXCNT);
|
|
mutex_destroy(&drvdata->mutex);
|
|
video_remove_capabilities_sysfs(pdev);
|
|
if (device_register_index == 0) {
|
|
class_destroy(drvdata->class);
|
|
}
|
|
devm_kfree(&pdev->dev, drvdata);
|
|
video_info("exit %s\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static const struct of_device_id video_of_match[] = {
|
|
{ .compatible = "thead,video", },
|
|
{ /* sentinel */ },
|
|
};
|
|
|
|
static struct platform_driver video_driver = {
|
|
.probe = video_probe,
|
|
.remove = video_remove,
|
|
.driver = {
|
|
.name = VIDEO_DRIVER_NAME,
|
|
.owner = THIS_MODULE,
|
|
.of_match_table = of_match_ptr(video_of_match),
|
|
}
|
|
};
|
|
|
|
static int __init video_init_module(void)
|
|
{
|
|
int ret = 0;
|
|
|
|
video_info("enter %s\n", __func__);
|
|
ret = platform_driver_register(&video_driver);
|
|
if (ret) {
|
|
video_err("register platform driver failed.\n");
|
|
return ret;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit video_exit_module(void)
|
|
{
|
|
video_info("enter %s\n", __func__);
|
|
platform_driver_unregister(&video_driver);
|
|
}
|
|
|
|
module_init(video_init_module);
|
|
module_exit(video_exit_module);
|
|
|
|
MODULE_AUTHOR("Liu Yitong");
|
|
MODULE_DESCRIPTION("THEAD-VIDEO");
|
|
MODULE_LICENSE("GPL");
|