Files
vi-kernel/vvcam/common/vvnative.c
2022-09-13 10:34:22 +08:00

642 lines
19 KiB
C
Executable File

/****************************************************************************
*
* The MIT License (MIT)
*
* Copyright (c) 2020 VeriSilicon Holdings Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
*****************************************************************************
*
* The GPL License (GPL)
*
* Copyright (c) 2020 VeriSilicon Holdings Co., Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program;
*
*****************************************************************************
*
* Note: This software is released under dual MIT and GPL licenses. A
* recipient may use this file under the terms of either the MIT license or
* GPL License. If you wish to use only one license not the other, you can
* indicate your decision by deleting one of the above license notices in your
* version of this file.
*
*****************************************************************************/
#include <linux/module.h> /* Module support */
#include <linux/version.h> /* Kernel version */
#include <linux/cdev.h> /* Charactor device support */
#include <linux/vermagic.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/ioctl.h>
#include <linux/mm.h>
#include "vvnative.h"
#include "vvnative_cfg.h"
#include "vvnative_combo.h"
#ifdef SENSOR_USE_GPIO_I2C
#include "soc_i2c.h"
#endif
#ifndef MODULE_LICENSE
# define MODULE_LICENSE(x)
#endif /* MODULE_LICENSE */
#ifndef MODULE_VERSION
# define MODULE_VERSION(x)
#endif /* MODULE_VERSION */
#define DRIVER_AUTHOR "VeriSilicon IPD"
#define DRIVER_DESC "Verisilicon ISP driver"
#define DRIVER_LICENSE "GPL"
#define DRIVER_VERSION "1.0"
#ifdef MODULE
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE( DRIVER_LICENSE );
MODULE_INFO( vermagic, VERMAGIC_STRING);
#endif /* MODULE */
/* vvcam_isp_dev_major: device major number */
unsigned int vvcam_isp_dev_major = 0;
module_param(vvcam_isp_dev_major, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(vvcam_isp_dev_major, "vvcam ISP device major number");
/* vvcam_isp_dev_minor: device base(start) minor number */
unsigned int vvcam_isp_dev_minor = 0;
module_param(vvcam_isp_dev_minor, uint, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(vvcam_isp_dev_minor, "vvcam ISP device base minor number");
/* vvcam_isp_dev_count: counts */
unsigned int vvcam_isp_dev_count = VVCAM_ISP_DEVICES;
module_param(vvcam_isp_dev_count, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(vvcam_isp_dev_count, "vvcam ISP device counts");
struct vvcam_isp_dev *vvcam_isp_drv;
struct vvcam_common *vvcam_drv_common;
static int vvcam_isp_dev_open(struct inode *inode, struct file *file);
static int vvcam_isp_dev_release(struct inode *inode, struct file *file);
static long vvcam_isp_dev_ioctl(struct file * pFile, unsigned int cmd, unsigned long arg);
static int vvcam_isp_dev_mmap(struct file *pFile, struct vm_area_struct *vma);
static int vvcam_isp_devio_remap(int dev_no);
static int vvcam_isp_devio_unmap(int dev_no);
long (*vvnative_ioctl[])(struct vvcam_isp_dev *vvcam_isp_drv,unsigned int cmd, void *args)=
{
vvcam_combo_isp_ioctl, /*devid0*/
vvcam_combo_isp_ioctl, /*devid1*/
vvcam_combo_csi_ioctl, /*devid2*/
vvcam_combo_csi_ioctl, /*devid3*/
vvcam_combo_sensor_ioctl, /*devid4*/
vvcam_combo_sensor_ioctl, /*devid5*/
vvcam_combo_dwe_ioctl, /*devid6*/
vvcam_combo_vse_ioctl, /*devid7*/
vvcam_combo_soc_ioctl, /*devid8*/
};
int (*vvnative_init[])(struct vvcam_isp_dev *vvcam_isp_drv)=
{
vvcam_combo_isp_init, /*devid0*/
vvcam_combo_isp_init, /*devid1*/
vvcam_combo_csi_init, /*devid2*/
vvcam_combo_csi_init, /*devid3*/
vvcam_combo_sensor_init, /*devid4*/
vvcam_combo_sensor_init, /*devid5*/
vvcam_combo_dwe_init, /*devid6*/
vvcam_combo_vse_init, /*devid7*/
vvcam_combo_soc_init, /*devid8*/
};
int (*vvnative_deinit[])(struct vvcam_isp_dev *vvcam_isp_drv)=
{
vvcam_combo_isp_deinit, /*devid0*/
vvcam_combo_isp_deinit, /*devid1*/
vvcam_combo_csi_deinit, /*devid2*/
vvcam_combo_csi_deinit, /*devid3*/
vvcam_combo_sensor_deinit, /*devid4*/
vvcam_combo_sensor_deinit, /*devid5*/
vvcam_combo_dwe_deinit, /*devid6*/
vvcam_combo_vse_deinit, /*devid7*/
vvcam_combo_soc_deinit, /*devid8*/
};
/*TODO:expand dual camera devs*/
struct file_operations vvcam_isp_fops = {
.owner = THIS_MODULE,
.open = vvcam_isp_dev_open,
.release = vvcam_isp_dev_release,
.unlocked_ioctl = vvcam_isp_dev_ioctl,
.mmap = vvcam_isp_dev_mmap,
};
/*On-the-fly debug opitions*/
char * on_the_fly_str="VIV ISP local debug buffer";
static void vivdev_dump_buf(unsigned char *out_buf, unsigned char* dump_data, int len, unsigned char * phy_address)
{
int m;
int n;
char *buf;
buf = out_buf;
int llength = 0x10;
unsigned int udata;
sprintf (buf, "Phy Addr: 0x%lx, Va Addr:0x%lx, len:%d\n", (unsigned long)phy_address, dump_data, len);
buf += strlen(buf);
for (m=0; m<len; m++)
{
if(m%llength == 0)
{
sprintf(buf, "%04x| ", m);
buf += strlen(buf);
}
if((m%2) == 0){
sprintf (buf, " ");
buf += strlen(buf);
}
udata = (unsigned int)(*(dump_data+m));
sprintf (buf, "%02x", udata&0xff);
buf += strlen(buf);
if(m%llength == (llength-1))
{
sprintf (buf, " | ");
buf += strlen(buf);
for (n=llength;n>0;n--)
{
if ((*(dump_data+m+1-n)>31) && (*(dump_data+m+1-n)<127))
{
sprintf (buf, "%c", *(dump_data+m+1-n));
buf += strlen(buf);
}
else
{
sprintf (buf, ".");
buf += strlen(buf);
}
}
sprintf (buf, "\n");
buf += strlen(buf);
}
}
sprintf (buf, "\n");
buf += strlen(buf);
}
static ssize_t get_vivdev(struct device *dev,
struct device_attribute *attr, char *buf)
{
const char * name;
char dev_name_perfix[8];
int dev_index;
int ret;
if(NULL == vvcam_drv_common)
{
pr_info("-->%s Error, null pointer of vvcam_drv_common\n", __func__);
return 0;
}
mutex_lock(&vvcam_drv_common->vvmutex);
name = dev_name(dev);
pr_info("-->%s dev_name name:%s\n", __func__, name);
ret = sscanf(name, "%6s%d", dev_name_perfix, &dev_index);
pr_info("-->%s sscanf ret:%d, name:%s, id:%d\n", __func__, ret, dev_name_perfix, dev_index);
ret = sprintf(buf, "%s\n", vvcam_drv_common->viv_buf);
mutex_unlock(&vvcam_drv_common->vvmutex);
return ret;
}
static ssize_t set_vivdev(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
const char * name;
char dev_name_perfix[8];
int dev_index;
unsigned int data;
unsigned int addr;
unsigned int read_cnt;
unsigned char mode;
int ret;
if(NULL == vvcam_drv_common)
{
pr_info("-->%s Error, null pointer of vvcam_drv_common\n", __func__);
return 0;
}
mutex_lock(&vvcam_drv_common->vvmutex);
name = dev_name(dev);
pr_info("-->%s dev_name name:%s\n", __func__, name);
ret = sscanf(name, "%6s%d", dev_name_perfix, &dev_index);
pr_info("-->%s sscanf ret:%d, name:%s, id:%d\n", __func__, ret, dev_name_perfix, dev_index);
ret = sscanf(buf, "%c, 0x%x, 0x%x, 0x%d", &mode, &addr, &data, &read_cnt);
if(ret < 3)
{
sprintf(vvcam_drv_common->viv_buf, "Error input string %s The parameters number:%d, expect format: 'r/w, 0xAddress, 0xData, 0xReadCnt'",
buf, ret);
pr_info("Error input string %s The parameters number:%d, expect format: 'r/w, 0xAddress, 0xData, 0xReadNumber'",buf, ret);
mutex_unlock(&vvcam_drv_common->vvmutex);
return len;
}
sprintf(vvcam_drv_common->viv_buf, "init: %s, scanf ret:%d, mode:%c, addr:0x%x, data:0x%x, readcnt:0x%x", buf, ret, mode, addr, data, read_cnt);
pr_info("-->%s init: %s, scanf ret:%d, mode:%c, addr:0x%x, data:0x%x, readcnt:0x%x", __func__, buf, ret, mode, addr, data, read_cnt);
vivdev_dump_buf((unsigned char*)vvcam_drv_common->viv_buf, on_the_fly_str, strlen(on_the_fly_str), 0xaaaa);
mutex_unlock(&vvcam_drv_common->vvmutex);
return len;
}
static DEVICE_ATTR(vivdevio, S_IWUSR|S_IRUSR|S_IRGRP|S_IWGRP|S_IROTH, get_vivdev, set_vivdev);
static int vvcam_isp_dev_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
pr_info("-->%s: ISPdev%d opened\n", __func__, minor);
mutex_lock(&(vvcam_isp_drv + minor)->vvmutex);
if((vvcam_isp_drv + minor)->open_cnt >= VVISP_OPEN_LIMITATION)
{
pr_info("-->%s: ISPdev%d cannot open device\n", __func__, minor);
mutex_unlock(&(vvcam_isp_drv + minor)->vvmutex);
return -EBUSY;
}
if((vvcam_isp_drv + minor)->initialized == 0)
{
pr_info("-->%s: ISPdev%d device not initialized\n", __func__, minor);
mutex_unlock(&(vvcam_isp_drv + minor)->vvmutex);
return -EBUSY;
}
(vvcam_isp_drv + minor)->open_cnt++;
pr_info("-->%s: ISPdev%d update open_cnt to %d\n", __func__, minor, (vvcam_isp_drv + minor)->open_cnt);
try_module_get(THIS_MODULE);
mutex_unlock(&(vvcam_isp_drv + minor)->vvmutex);
return 0;
}
/* Called when a process closes the device file */
static int vvcam_isp_dev_release(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
pr_info("-->%s: ISPdev%d releaseing...\n", __func__, minor);
mutex_lock(&(vvcam_isp_drv + minor)->vvmutex);
if((vvcam_isp_drv + minor)->open_cnt <= 0)
{
pr_info("-->%s: ISPdev%d internal error\n", __func__, minor);
mutex_unlock(&(vvcam_isp_drv + minor)->vvmutex);
return -EBUSY;
}
(vvcam_isp_drv + minor)->open_cnt--;
module_put(THIS_MODULE);
mutex_unlock(&(vvcam_isp_drv + minor)->vvmutex);
return 0;
}
static long vvcam_isp_dev_ioctl(struct file * pFile, unsigned int cmd, unsigned long arg)
{
long ret;
struct inode *inode =pFile->f_mapping->host;
int minor = iminor(inode);
pr_info("-->%s: ISPdev%d Ioctl runs, cmd:%d, args:%ld...\n", __func__, minor, cmd, arg);
ret = (*(vvnative_ioctl+minor))(vvcam_isp_drv + minor, cmd, arg);
return ret;
};
static int vvcam_isp_dev_mmap(struct file *pFile, struct vm_area_struct *vma)
{
struct inode *inode =pFile->f_mapping->host;
int minor = iminor(inode);
ulong img_buf_base = vvnative_get_img_buf_base();
ulong img_buf_size = vvnative_get_img_buf_size();
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
unsigned long pfn_start = (img_buf_base >> PAGE_SHIFT) + vma->vm_pgoff;
unsigned long size = vma->vm_end - vma->vm_start;
int ret = 0;
if(size > img_buf_size)
{
size = img_buf_size;
}
pr_info("-->%s: ISPdev%d mmap runs...\n", __func__, minor);
pr_info("-->%s: mmap parameters: phy start:0x%lx, ph size:0x%lx, vm start: 0x%lx, size: 0x%lx, offset, page start: 0x%lx\n",
__func__, img_buf_base, img_buf_size, vma->vm_start, size, offset, pfn_start);
/* Map reserved image buffer memory. */
if (remap_pfn_range(vma, vma->vm_start,
(img_buf_base >> PAGE_SHIFT),
size, vma->vm_page_prot))
{
pr_err("-->%s: remap_pfn_range error!\n", __func__);
pr_info("-->%s: mmap parameters: phy start:0x%lx, ph size:0x%lx, vm start: 0x%lx, size: 0x%lx, offset\n",
__func__, img_buf_base, img_buf_size, vma->vm_start, size, offset);
return -EIO;
}
return ret;
};
static int vvcam_isp_devio_remap(int dev_no)
{
void __iomem *addr;
ulong reg_base = vvnative_get_reg_base_by_idx(dev_no);
ulong reg_size = vvnative_get_reg_size_by_idx(dev_no);
if(NULL == vvcam_isp_drv)
{
pr_err("-->%s: NULL pointer!\n", __func__);
return -1;
}
(vvcam_isp_drv + dev_no)->phy_address = reg_base;
(vvcam_isp_drv + dev_no)->size = reg_size;
pr_info("ioremap of dev: %d, phy address:0x%lx, size:0x%lx\n", dev_no, reg_base, reg_size);
if((0 == reg_base) ||(0 == reg_size))
{
(vvcam_isp_drv + dev_no)->base_address = NULL;
pr_info("ioremap of dev: %d, phy address is 0\n", dev_no);
}else{
addr = ioremap(reg_base, reg_size);
pr_info("ioremap of dev: %d, address:0x%lx\n", dev_no, (unsigned long)addr);
(vvcam_isp_drv + dev_no)->base_address = addr;
}
return 0;
}
static int vvcam_isp_devio_unmap(int dev_no)
{
if((NULL == vvcam_isp_drv))
{
pr_err("-->%s: NULL pointer!\n", __func__);
return -1;
}
if(NULL != (vvcam_isp_drv + dev_no)->base_address)
{
iounmap((vvcam_isp_drv + dev_no)->base_address);
}
return 0;
}
/*!
*******************************************************************************
** First function called by the OS. This function registers the device and
** vendor id that this driver handles.
*/
int __init vvcam_isp_dev_initialize( void )
{
int result = 0;
struct device *dev;
dev_t devt;
int retval;
int dev_idx = 0;
struct class *vvisp_class;
pr_info("-->%s enter\n", __func__);
pr_info("-->%s : %d, registerMemBase = 0x%lx, dev number:%d\n", __func__, __LINE__, VVISP0_BASE, VVCAM_ISP_DEVICES);
/**********************************************************************
* register/alloc the device major number and range, if
* major = 0, alloc from kernel to get an available device major number
* major != 0, use the specific value as major number, this value may be invalid
**********************************************************************/
vvcam_isp_drv = kzalloc(sizeof(struct vvcam_isp_dev) * VVCAM_ISP_DEVICES, GFP_KERNEL);
if (!vvcam_isp_drv)
{
return -ENOMEM;
}
vvcam_drv_common = kzalloc(sizeof(struct vvcam_common), GFP_KERNEL);
if (!vvcam_drv_common)
{
return -ENOMEM;
}
if (0 == vvcam_isp_dev_major)
{
result = alloc_chrdev_region(&devt, 0, VVCAM_ISP_DEVICES, DRIVER_NAME);
if (result)
{
goto fail_check;
}
vvcam_isp_dev_major = MAJOR(devt);
vvcam_isp_dev_minor = MINOR(devt);
}
else
{
devt = MKDEV(vvcam_isp_dev_major, vvcam_isp_dev_minor);
result = register_chrdev_region(devt, VVCAM_ISP_DEVICES, DRIVER_NAME);
if (result)
{
result = -EBUSY;
goto fail_check;
}
}
pr_info("--> vvcam ISP init, major id: %d, minor id: %d\n", MAJOR(devt), MINOR(devt));
vvisp_class = class_create(THIS_MODULE, DRIVER_NAME);
if (IS_ERR(vvisp_class))
{
pr_info("-->%s : %d, class_create error!\n", __func__, __LINE__);
goto fail_check;
}
dev_idx = 0;
for(dev_idx = 0; dev_idx < VVCAM_ISP_DEVICES; dev_idx++)
{
if(strcmp(vvnative_get_dev_name_by_idx(dev_idx), VVNATIVE_NA) == 0)
{
pr_info("--> subisp%d is absent, id number:%s, skip..\n", dev_idx, vvnative_get_dev_name_by_idx(dev_idx) );
(vvcam_isp_drv + dev_idx)->initialized = 0;
continue;
}
(vvcam_isp_drv + dev_idx)->devt = MKDEV(vvcam_isp_dev_major, vvcam_isp_dev_minor + dev_idx);
pr_info("--> subisp%d reg, major id: %d, minor id: %d, dev_name:%s\n", dev_idx,
MAJOR((vvcam_isp_drv + dev_idx)->devt), MINOR((vvcam_isp_drv + dev_idx)->devt),
vvnative_get_dev_name_by_idx(dev_idx));
cdev_init(&(vvcam_isp_drv + dev_idx)->cdev, &vvcam_isp_fops);
(vvcam_isp_drv + dev_idx)->cdev.owner = THIS_MODULE;
retval = cdev_add(&(vvcam_isp_drv + dev_idx)->cdev, (vvcam_isp_drv + dev_idx)->devt, 1);
if (retval)
{
pr_info("-->%s : %d, cdev_add error!\n", __func__, __LINE__);
goto fail_check;
}
(vvcam_isp_drv + dev_idx)->class = vvisp_class;
dev = device_create((vvcam_isp_drv + dev_idx)->class, NULL, (vvcam_isp_drv + dev_idx)->devt,
(vvcam_isp_drv + dev_idx), "%s%d", vvnative_get_dev_name_by_idx(dev_idx), vvnative_get_dev_idx(dev_idx));
if (IS_ERR(dev))
{
pr_info("-->%s : %d, device_create error!\n", __func__, __LINE__);
class_destroy(vvcam_isp_drv->class);
goto fail_check;
}
pr_info("-->dev create for subisp %s %d done\n", DRIVER_NAME, dev_idx);
(vvcam_isp_drv + dev_idx)->dev = dev;
if(sysfs_create_file(&(dev->kobj), &dev_attr_vivdevio.attr)) {
pr_info("-->%s : %d, sysfs_create_file error!\n", __func__, __LINE__);
goto fail_check;
}
/* init */
mutex_init(&(vvcam_isp_drv + dev_idx)->vvmutex);
(vvcam_isp_drv + dev_idx)->open_cnt = 0;
(vvcam_isp_drv + dev_idx)->initialized = 1;
(vvcam_isp_drv + dev_idx)->dev_idx = vvnative_get_dev_idx(dev_idx);
result = vvcam_isp_devio_remap(dev_idx);
if(result)
{
pr_err("-->%s: vvcam_isp_devio_remap error!\n", __func__);
goto fail_check;
}
/* submodule initialization */
result = (*(vvnative_init+dev_idx))(vvcam_isp_drv + dev_idx);
if(result)
{
pr_err("-->%s: vvnative_init error with subdev:%d!\n", __func__, dev_idx);
goto fail_check;
}
}
/* on-the-fly debug init */
mutex_init(&vvcam_drv_common->vvmutex);
sprintf(vvcam_drv_common->viv_buf, "vivisp_buf");
return result;
fail_check:
/* Unregister char driver */
pr_info("-->vvcam_isp_dev_initialize error\n");
unregister_chrdev_region(devt, VVCAM_ISP_DEVICES);
return result;
}
/*!
*******************************************************************************
** Unload the driver module
*/
void __exit vvcam_isp_dev_cleanup( void )
{
int result = 0;
int dev_idx = 0;
pr_info("-->%s enter\n", __func__);
if(NULL == vvcam_isp_drv)
{
pr_info("-->%s Internal error, NULL pointer\n", __func__);
return;
}
for(dev_idx = 0; dev_idx < VVCAM_ISP_DEVICES; dev_idx++)
{
if(0 == (vvcam_isp_drv + dev_idx)->initialized)
{
continue;
}
/* submodule un-initialization */
result = (*(vvnative_deinit+dev_idx))(vvcam_isp_drv + dev_idx);
if(result)
{
pr_err("-->%s: vvnative_deinit error with subdev:%d!\n", __func__, dev_idx);
}
result = vvcam_isp_devio_unmap(dev_idx);
if(result)
{
pr_err("-->%s: vvcam_isp_devio_unmap error!\n", __func__);
}
device_destroy((vvcam_isp_drv + dev_idx)->class, (vvcam_isp_drv + dev_idx)->devt);
cdev_del(&(vvcam_isp_drv + dev_idx)->cdev);
/* remove the char device structure (has been added) */
unregister_chrdev_region((vvcam_isp_drv + dev_idx)->devt, VVCAM_ISP_DEVICES);
(vvcam_isp_drv + dev_idx)->dev = NULL;
(vvcam_isp_drv + dev_idx)->open_cnt = 0;
}
class_destroy(vvcam_isp_drv->class);
vvcam_isp_drv->class = NULL;
kfree(vvcam_isp_drv);
vvcam_isp_drv = NULL;
kfree(vvcam_drv_common);
vvcam_drv_common = NULL;
return;
}
module_init(vvcam_isp_dev_initialize);
module_exit(vvcam_isp_dev_cleanup);
MODULE_LICENSE ("GPL");