/* * Copyright (C) 2021 Alibaba Group Holding Limited * Author: liuyitong * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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");