MTK Camera 模块驱动、设备与总线结构:
时间:10-02
整理:3721RD
点击:
mtk CAMERA 模块驱动、设备与总线结构:
a) 驱动的注册
在(\custom\common\kernel\imgsensor\src\Kd_sensorlist.c)CAMERA_HW_I2C_init 这个
static int __init CAMERA_HW_i2C_init(void)
{
struct proc_dir_entry *prEntry;
//i2c_register_board_info(CAMERA_I2C_BUSNUM, &kd_camera_dev, 1);
i2c_register_board_info(SUPPORT_I2C_BUS_NUM1, &i2c_devs1, 1);
//i2c_register_board_info(SUPPORT_I2C_BUS_NUM2, &i2c_devs2, 1);
if(platform_driver_register(&g_stCAMERA_HW_Driver)){
PK_ERR("failed to register CAMERA_HW driver\n");
return -ENODEV;
}
//if(platform_driver_register(&g_stCAMERA_HW_Driver2)){
// PK_ERR("failed to register CAMERA_HW driver\n");
// return -ENODEV;
//}
//Register proc file for main sensor register debug
prEntry = create_proc_entry("driver/camsensor", 0, NULL);
if (prEntry) {
prEntry->read_proc = CAMERA_HW_DumpReg_To_Proc;
prEntry->write_proc = CAMERA_HW_Reg_Debug;
}
else {
PK_ERR("add /proc/driver/camsensor entry fail \n");
}
//Register proc file for sub sensor register debug
prEntry = create_proc_entry("driver/camsensor2", 0, NULL);
if (prEntry) {
prEntry->read_proc = CAMERA_HW_DumpReg_To_Proc;
prEntry->write_proc = CAMERA_HW_Reg_Debug2;
}
else {
PK_ERR("add /proc/driver/camsensor2 entry fail \n");
}
atoMIC_set(&g_CamHWOpend, 0);
//atomic_set(&g_CamHWOpend2, 0);
atomic_set(&g_CamDrvOpenCnt, 0);
//atomic_set(&g_CamDrvOpenCnt2, 0);
atomic_set(&g_CamHWOpening, 0);
return 0;
}
函数里通过 Platform_driver_register(&g_stCAMERA_HW_Driver)把 Camera 模块驱动注册 到 Platform 总线上。
而 g_stCAMERA_HW_Driver 是对结构体 Platform_driver 这个结构体的填充。
///////////////////////////////// i2c_register_board_info 的解释
//i2c_board_info用于构建信息表来列出存在的I2C设备。这一信息用于增长新型I2C驱动的驱动模型树。对于主板,它使用i2c_register_board_info()来静态创建。对于子板,利用已知的适配器使用i2c_new_device()动态创建。
//I2C 设备创建模板
struct i2c_board_info {
char type[I2C_NAME_SIZE]; //芯片类型,用于初始化i2c_CLIent.name
unsigned short flags; //用于初始化i2c_client.flags
unsigned short addr; //存储于i2c_client.addr
void *platform_data; //存储于i2c_client.dev.platform_data
struct dev_archdata *archdata; //拷贝至i2c_client.dev.archdata
int irq; //存储于i2c_client.irq
};
//使用linux I2C驱动栈,系统可以在初始化时宣告板载信息表。这些应该在靠近arch_initcall()时的板子相关的初始化代码或同等情况时,在I2C适配器驱动被注册之前被执行。例如,主板初始化代码可以定义//几个设备,也可以在叠板的每个子板初始化代码中定义。
//I2C设备会在相关的总线适配器被注册后创建。此后,标准驱动模型工具通常绑定新型I2C驱动至I2C设备。对于使用这一函数宣告的设备,在动态分配的情况下总线号是不可用的。
//传递的板子信息可以安全的是__initdata,但是由于不能拷贝,要小心嵌入式指针(如platform_data,functions等)
//静态的宣告I2C设备
int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len);
@busnum: 指定这些设备属于哪个总线
@info: I2C设备描述符向量
@len: 向量中描述符的数量;为了预留特定的总线号,可以是0。
在你对应的machine配置里会执行“i2c_register_board_info”一个函数,它会将一个i2c_board_info的结构体注册进系统,
可以发现,在目录/sys/bus/i2c/devices下的设备就是这个i2c_board_info结构体里所描述的I2C设备,
而/sys/bus/i2c/devices下的设备名字就是根据i2c_board_info结构体中定义的I2C Address来命名的。
所以添加一个I2C设备时,除了需要编写这个I2C设备的驱动之外,还需要在machine里面加入I2C设备的i2c_board_info内容。
/////////////////////////////////////////////////////////////////////
///////////////////////////////platform_driver_register 的解释
platform_driver_register()
是来注册设备的驱动程序
platform_device_register()
是来注册设备硬件,告诉kernel,当前有什么设备
当某个设备connect后,必须在device和driver的两条线上都有匹配的东西后,才算是匹配上,并且开始使用driver的probe等函数进行硬件初始化工作。
////////////////////////
// (Kernel\include\linux\Platform_device.h)
static struct platform_driver g_stCAMERA_HW_Driver = {
.probe = CAMERA_HW_probe,
.remove = CAMERA_HW_remove,
.suspend = CAMERA_HW_suspend,
.resume = CAMERA_HW_resume,
.driver = {
.name = "image_sensor",
.owner = THIS_MODULE,
}
};
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
static int CAMERA_HW_probe(struct platform_device *pdev)
{
return i2c_add_driver(&CAMERA_HW_i2c_driver);
}
static int CAMERA_HW_remove(struct platform_device *pdev)
{
i2c_del_driver(&CAMERA_HW_i2c_driver);
return 0;
}
static int CAMERA_HW_suspend(struct platform_device *pdev, pm_message_t mesg)
{
return 0;
}
static int CAMERA_HW_resume(struct platform_device *pdev)
{
return 0;
}
b) 设备的注册:
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
//kernel\arch\sh\boards\mach-ap325rxa\Setup.c
static struct platform_device ap325rxa_camera[] = {
{
.name = "soc-camera-pdrv",
.id = 0,
.dev = {
.platform_data = &ov7725_link,
},
}, {
.name = "soc-camera-pdrv",
.id = 1,
.dev = {
.platform_data = &camera_link,
},
},
};
static struct soc_camera_link camera_link = {
.bus_id = 0,
.add_device = ap325rxa_camera_add,
.del_device = ap325rxa_camera_del,
.module_name = "soc_camera_platform",
.priv = &camera_info,
};
static int ap325rxa_camera_add(struct soc_camera_device *icd)
{
int ret = soc_camera_platform_add(icd, &camera_device, &camera_link,
ap325rxa_camera_release, 0);
if (ret < 0)
return ret;
ret = camera_probe();
if (ret < 0)
soc_camera_platform_del(icd, camera_device, &camera_link);
return ret;
}
static void ap325rxa_camera_del(struct soc_camera_device *icd)
{
soc_camera_platform_del(icd, camera_device, &camera_link);
}
static struct soc_camera_platform_info camera_info = {
.format_name = "UYVY",
.format_depth = 16,
.format = {
.code = V4L2_MBUS_FMT_UYVY8_2X8,
.colorspace = V4L2_COLORSPACE_SMPTE170M,
.field = V4L2_FIELD_NONE,
.width = 640,
.height = 480,
},
.mbus_param = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
V4L2_MBUS_DATA_ACTIVE_HIGH,
.mbus_type = V4L2_MBUS_PARALLEL,
.set_capture = camera_set_capture,
};
c) 总线的匹配:
既 然 是 驱 动 Platform_device 那 对 应 的 设 备 必 然 是 挂 载 Platform 总 线 上 的Platform_device,Platform 总线是 Linux 系统提供的一种机制,不同于 I2C、I2S 等总线,它 是一种虚拟的总线。
Linux 系统为 Platform 总线定义了一个 bus_type 的实例 Platform_bus_type:
//(Kernel\drivers\base\platform.c)
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup();
error = device_register(&platform_bus);
if (error)
return error;
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
return error;
}
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
Platform 总线通过 platform_match 这个成员函数来确定 platform_device 与 platform_driver 如何进行匹配:
static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct platform_device *pdev = to_platform_device(dev);
int rc;
/* Some devices have extra OF data and an OF-style MODALIAS */
rc = of_device_uevent_modalias(dev,env);
if (rc != -ENODEV)
return rc;
add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX,
pdev->name);
return 0;
}
static const struct dev_pm_ops platform_dev_pm_ops = {
.runtime_suspend = pm_generic_runtime_suspend,
.runtime_resume = pm_generic_runtime_resume,
.runtime_idle = pm_generic_runtime_idle,
USE_PLATFORM_PM_SLEEP_OPS
};
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev);
}
void platform_device_unregister(struct platform_device *pdev)
{
platform_device_del(pdev);
platform_device_put(pdev);
}
从上图可以清晰的了解到 Camera 的一个工作流程主要分为这么七步:
1.打开 Camera power LDO,让 Camera 有能量保证。
2.打开 IIC,设置 PDN 引脚,使 Camera 退出出 Standby 模式,按照要求让 Reset 脚 做一个复位动作。
3.读一下 sensor 的版本 ID,这样可以让你确认是否连接上你想要的 sensor。
4.对 Sensor 进行初始化下载最基本的参数让 Sensor 工作起来,可能包括软复位。
5.下载 preview 的参数,为预览动作准备。
6.下载 Capture 的参数,为拍照动作准备。
7.设置 PDN 引脚,使 Sensor 进入 Standby 模式,或者关掉 LDO 等动作,退出 Camera。
d)设备驱动
我们都知道,Linux 内核是通过模块的机制来加载设备驱动的,那么接下来我们就从设备模块加载的角度来看下 Camera 工作流程的驱动代码是如何工作的。
在-alps\mediatek\custom\common\kernel\imgsensor\src\kd_sensorlist.c 中可以看到:
module_init(CAMERA_HW_i2C_init);
module_exit(CAMERA_HW_i2C_exit);
在这里 Linux 内核加载和卸载 Camera 模块。
struct i2c_driver CAMERA_HW_i2c_driver = {
.probe = CAMERA_HW_i2c_probe,
.remove = CAMERA_HW_i2c_remove,
.driver.name = CAMERA_HW_DRVNAME1,
.id_table = CAMERA_HW_i2c_id,
};
static int CAMERA_HW_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int i4RetValue = 0;
PK_DBG("[CAMERA_HW] Attach I2C \n");
//get sensor i2c client
SPIn_lock(&kdsensor_drv_lock);
g_pstI2Cclient = client;
//set I2C clock rate
g_pstI2Cclient->timing = 300;//200k
spin_unlock(&kdsensor_drv_lock);
//Register char driver
i4RetValue = RegisterCAMERA_HWCharDrv();
if(i4RetValue){
PK_ERR("[CAMERA_HW] register char device failed!\n");
return i4RetValue;
}
//spin_lock_init(&g_CamHWLock);
PK_DBG("[CAMERA_HW] Attached! \n");
return 0;
}
static int CAMERA_HW_i2c_remove(struct i2c_client *client)
{
return 0;
}
static const struct i2c_device_id CAMERA_HW_i2c_id[] = {{CAMERA_HW_DRVNAME1,0},{}};
inline static int RegisterCAMERA_HWCharDrv(void)
{
struct device* sensor_device = NULL;
#if CAMERA_HW_DYNAMIC_ALLOCATE_DEVNO
if( alloc_chrdev_region(&g_CAMERA_HWdevno, 0, 1,CAMERA_HW_DRVNAME1) )
{
PK_DBG("[CAMERA SENSOR] Allocate device no failed\n");
return -EAGAIN;
}
#else
if( register_chrdev_region( g_CAMERA_HWdevno , 1 , CAMERA_HW_DRVNAME1) )
{
PK_DBG("[CAMERA SENSOR] Register device no failed\n");
return -EAGAIN;
}
#endif
//Allocate driver
g_pCAMERA_HW_CharDrv = cdev_alloc();
if(NULL == g_pCAMERA_HW_CharDrv)
{
unregister_chrdev_region(g_CAMERA_HWdevno, 1);
PK_DBG("[CAMERA SENSOR] Allocate mem for kobject failed\n");
return -ENOMEM;
}
//Attatch file operation.
cdev_init(g_pCAMERA_HW_CharDrv, &g_stCAMERA_HW_fops);
g_pCAMERA_HW_CharDrv->owner = THIS_MODULE;
//Add to system
if(cdev_add(g_pCAMERA_HW_CharDrv, g_CAMERA_HWdevno, 1))
{
PK_DBG("[MT6516_IDP] Attatch file operation failed\n");
unregister_chrdev_region(g_CAMERA_HWdevno, 1);
return -EAGAIN;
}
sensor_class = class_create(THIS_MODULE, "sensordrv");
if (IS_ERR(sensor_class)) {
int ret = PTR_ERR(sensor_class);
PK_DBG("Unable to create class, err = %d\n", ret);
return ret;
}
sensor_device = device_create(sensor_class, NULL, g_CAMERA_HWdevno, NULL, CAMERA_HW_DRVNAME1);
return 0;
}
在 RegisterCAMERA_HWCharDrv()中cdev_init(g_pCAMERA_HW_CharDrv, &g_stCAMERA_HW_fops);
对设备进行初始化,并将g_stCAMERA_HW_fops 这个文件操作函数作为上层对 Camera 设备操作的接口留给上层进 行调用:
static const struct file_operations g_stCAMERA_HW_fops =
{
.owner = THIS_MODULE,
.open = CAMERA_HW_Open,
.release = CAMERA_HW_Release,
.unlocked_ioctl = CAMERA_HW_Ioctl
};
其中成员函数 open()只是初始化一个原子变量留给系统调用。
ioctl()才是整个 Camera驱动的入口:CAMERA_HW_Ioctl()是上层文件操作系统操作底层硬件的方法,它先对 Camera 需要的Buffer 做一个初始化,然后建立对 Cameraopen、getinfo 等操作的接口:
static long CAMERA_HW_Ioctl(
struct file * a_pstFile,
unsigned int a_u4Command,
unsigned long a_u4Param
)
{
int i4RetValue = 0;
void * pBuff = NULL;
u32 *pIdx = NULL;
mutex_lock(&kdCam_Mutex);
if(_IOC_NONE == _IOC_DIR(a_u4Command)) {
}
else {
pBuff = kmalloc(_IOC_SIZE(a_u4Command),GFP_KERNEL);
if(NULL == pBuff) {
PK_DBG("[CAMERA SENSOR] ioctl allocate mem failed\n");
i4RetValue = -ENOMEM;
goto CAMERA_HW_Ioctl_EXIT;
}
if(_IOC_WRITE & _IOC_DIR(a_u4Command)){
if(copy_fROM_user(pBuff , (void *) a_u4Param, _IOC_SIZE(a_u4Command))) {
kfree(pBuff);
PK_DBG("[CAMERA SENSOR] ioctl copy from user failed\n");
i4RetValue = -EFAULT;
goto CAMERA_HW_Ioctl_EXIT;
}
}
}
pIdx = (u32*)pBuff;
switch(a_u4Command) {
#if 0
case KDIMGSENSORIOC_X_POWER_ON:
i4RetValue = kdModulePowerOn((CAMERA_DUAL_CAMERA_SENSOR_ENUM) *pIdx, true, CAMERA_HW_DRVNAME);
break;
case KDIMGSENSORIOC_X_POWER_OFF:
i4RetValue = kdModulePowerOn((CAMERA_DUAL_CAMERA_SENSOR_ENUM) *pIdx, false, CAMERA_HW_DRVNAME);
break;
#endif
case KDIMGSENSORIOC_X_SET_DRIVER:
i4RetValue = kdSetDriver((unsigned int*)pBuff);
//在 KdSetDriver()中通过判断 name 和 ID 匹配具体型号的 sensor 的驱动,判断它是主摄还 是次摄,并对它进行初始化:
break;
case KDIMGSENSORIOC_T_OPEN:
i4RetValue = adopt_CAMERA_HW_Open();
break;
case KDIMGSENSORIOC_X_GETINFO:
i4RetValue = adopt_CAMERA_HW_GetInfo(pBuff);
break;
case KDIMGSENSORIOC_X_GETRESOLUTION:
i4RetValue = adopt_CAMERA_HW_GetResolution(pBuff);
break;
case KDIMGSENSORIOC_X_FEATURECONCTROL:
i4RetValue = adopt_CAMERA_HW_FeatureControl(pBuff);
break;
case KDIMGSENSORIOC_X_CONTROL:
i4RetValue = adopt_CAMERA_HW_Control(pBuff);
break;
case KDIMGSENSORIOC_T_CLOSE:
i4RetValue = adopt_CAMERA_HW_Close();
break;
case KDIMGSENSORIOC_T_CHECK_IS_ALIVE:
i4RetValue = adopt_CAMERA_HW_CheckIsAlive();
break;
case KDIMGSENSORIOC_X_GET_SOCKET_POS:
i4RetValue = kdGetSocketPostion((unsigned int*)pBuff);
break;
case KDIMGSENSORIOC_X_SET_I2CBUS:
//i4RetValue = kdSetI2CBusNum(*pIdx);
break;
case KDIMGSENSORIOC_X_RELEASE_I2C_TRIGGER_LOCK:
//i4RetValue = kdReleaseI2CTriggerLock();
break;
default :
PK_DBG("No such command \n");
i4RetValue = -EPERM;
break;
}
if(_IOC_READ & _IOC_DIR(a_u4Command)) {
if(copy_to_user((void __user *) a_u4Param , pBuff , _IOC_SIZE(a_u4Command))) {
kfree(pBuff);
PK_DBG("[CAMERA SENSOR] ioctl copy to user failed\n");
i4RetValue = -EFAULT;
goto CAMERA_HW_Ioctl_EXIT;
}
}
kfree(pBuff);
CAMERA_HW_Ioctl_EXIT:
mutex_unlock(&kdCam_Mutex);
return i4RetValue;
}
通过判断 Sensor 状态的逻辑值来进行具体的操作,对于这个值的定义在:
Mediatek\custom\common\kernel\imgsensor\inc\Kd_imgsensor.h 中
//sensorOpen
//This command will TBD
#define KDIMGSENSORIOC_T_OPEN _IO(IMGSENSORMAGIC,0)
//sensorGetInfo
//This command will TBD
#define KDIMGSENSORIOC_X_GETINFO _IOWR(IMGSENSORMAGIC,5,ACDK_SENSOR_GETINFO_STRUCT)
//sensorGetResolution
//This command will TBD
#define KDIMGSENSORIOC_X_GETRESOLUTION _IOWR(IMGSENSORMAGIC,10,ACDK_SENSOR_RESOLUTION_INFO_STRUCT)
//sensorFeatureControl
//This command will TBD
#define KDIMGSENSORIOC_X_FEATURECONCTROL _IOWR(IMGSENSORMAGIC,15,ACDK_SENSOR_FEATURECONTROL_STRUCT)
//sensorControl
//This command will TBD
#define KDIMGSENSORIOC_X_CONTROL _IOWR(IMGSENSORMAGIC,20,ACDK_SENSOR_CONTROL_STRUCT)
//sensorClose
//This command will TBD
#define KDIMGSENSORIOC_T_CLOSE _IO(IMGSENSORMAGIC,25)
//sensorSearch
#define KDIMGSENSORIOC_T_CHECK_IS_ALIVE _IO(IMGSENSORMAGIC, 30)
//set sensor driver
#define KDIMGSENSORIOC_X_SET_DRIVER _IOWR(IMGSENSORMAGIC,35,SENSOR_DRIVER_INDEX_STRUCT)
//get socket postion
#define KDIMGSENSORIOC_X_GET_SOCKET_POS _IOWR(IMGSENSORMAGIC,40,u32)
//set I2C bus
#define KDIMGSENSORIOC_X_SET_I2CBUS _IOWR(IMGSENSORMAGIC,45,u32)
//set I2C bus
#define KDIMGSENSORIOC_X_RELEASE_I2C_TRIGGER_LOCK _IO(IMGSENSORMAGIC,50)
//Set Shutter Gain Wait Done
#define KDIMGSENSORIOC_X_SET_SHUTTER_GAIN_WAIT_DONE _IOWR(IMGSENSORMAGIC,55,u32)//HDR
//set mclk
#define KDIMGSENSORIOC_X_SET_MCLK_PLL _IOWR(IMGSENSORMAGIC,60,ACDK_SENSOR_MCLK_STRUCT)
在 KdSetDriver()中通过判断 name 和 ID 匹配具体型号的 sensor 的驱动,判断它是主摄还 是次摄,并对它进行初始化:
if ( MAX_NUM_OF_SUPPORT_SENSOR > drvIdx ) {
if (NULL == pSensorList[drvIdx].SensorInit) {
PK_ERR("ERROR:kdSetDriver()\n");
return -EIO;
}
pSensorList[drvIdx].SensorInit(&g_pInvokeSensorFunc);
if (NULL == g_pInvokeSensorFunc) {
PK_ERR("ERROR:NULL g_pSensorFunc[%d]\n",i);
return -EIO;
}
通过 NAME 和 ID 匹配完成后会将 PSENSOR_FUNCTION_STRUCT *pfFunc 这个结构体匹 配到具体型号的驱动代码中:
SENSOR_FUNCTION_STRUCT SensorFuncOV5648mipi=
{
OV5648MIPIOpen,
OV5648MIPIGetInfo,
OV5648MIPIGetResolution,
OV5648MIPIFeatureControl,
OV5648MIPIControl,
OV5648MIPIClose
};
到这里,整个 Camera 驱动从总线注册到完成具体 sensor 的初始化的流程就完成了,CAMERA_HW_Ioctl()中其他的 ioctl 操作函数最后都会在$sensor$_sensor.c 中实现。
1、 修改系统配置文件 ProjectConfig.mk:
-alps\mediatek\config\$project$\ProjectConfig.mk
2、 检查、配置供电文件:
-alps\mediatek\custom\$project$\Kernel\Camera\Camera\kd_camera_hw.c
其实在 kd_camera_hw.c 中只有一个函数 kdCISModulePowerOn(),在这个 函数中需要注意的是通过 GPIO 口控制 PDN 和 RST 引脚的时候,对于其相关的
定义,由其在切换平台的时候。例如 MT6573 和 MT6575 的定义顺序就不同。
b)OV5647GetInfo
UINT32 OV5647GetInfo(MSDK_SCENARIO_ID_ENUM ScenarioId,
MSDK_SENSOR_INFO_STRUCT *pSensorInfo,
MSDK_SENSOR_CONFIG_STRUCT *pSensorConfigData)
第一个参数 ScenarioId 来自于 MSDK_SCENARIO_ID_ENUM 这个数组,在kd_imgsensor_define.h 中是这样定义的:
#define MSDK_SCENARIO_ID_ENUM ACDK_SCENARIO_ID_ENUM
typedef enum
{
ACDK_SCENARIO_ID_CAMERA_PREVIEW=0,
ACDK_SCENARIO_ID_VIDEO_PREVIEW,
ACDK_SCENARIO_ID_VIDEO_CAPTURE_MPEG4,
ACDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG,
ACDK_SCENARIO_ID_CAMERA_CAPTURE_MEM,
ACDK_SCENARIO_ID_CAMERA_BURST_CAPTURE_JPEG,
ACDK_SCENARIO_ID_VIDEO_DECODE_MPEG4,
ACDK_SCENARIO_ID_VIDEO_DECODE_H263,
ACDK_SCENARIO_ID_VIDEO_DECODE_H264,
ACDK_SCENARIO_ID_VIDEO_DECODE_WMV78,
ACDK_SCENARIO_ID_VIDEO_DECODE_WMV9,
ACDK_SCENARIO_ID_VIDEO_DECODE_MPEG2,
ACDK_SCENARIO_ID_IMAGE_YUV2RGB,
ACDK_SCENARIO_ID_IMAGE_RESIZE,
ACDK_SCENARIO_ID_IMAGE_ROTATE,
ACDK_SCENARIO_ID_IMAGE_POST_PROCESS, ACDK_SCENARIO_ID_JPEG_RESIZE, ACDK_SCENARIO_ID_JPEG_DECODE, ACDK_SCENARIO_ID_JPEG_PARSE, ACDK_SCENARIO_ID_JPEG_ENCODE, ACDK_SCENARIO_ID_JPEG_ENCODE_THUMBNAIL, ACDK_SCENARIO_ID_DRIVER_IO_CONTROL, ACDK_SCENARIO_ID_DO_NOT_CARE, ACDK_SCENARIO_ID_IMAGE_DSPL_BUFFER_ALLOC, ACDK_SCENARIO_ID_TV_OUT, ACDK_SCENARIO_ID_MAX,
ACDK_SCENARIO_ID_VIDOE_ENCODE_WITHOUT_PREVIEW, ACDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG_BACK_PREVIEW, ACDK_SCENARIO_ID_VIDEO_DECODE_RV8, ACDK_SCENARIO_ID_VIDEO_DECODE_RV9, ACDK_SCENARIO_ID_CAMERA_ZSD,
} ACDK_SCENARIO_ID_ENUM;
通过这个数组定义 Camera 的各种模式,并且给他们从 0 开始给一个模拟的 ID,通过这个
ScenarioID 来控制 Camera 的工作模式是在拍照、摄像等等。
想要了解*pSensorInfo 这个指针的内容就得看 MSDK_SENSOR_INFO_STRUCT 的定义
#define MSDK_SENSOR_INFO_STRUCT ACDK_SENSOR_INFO_STRUCT typedef struct
{
MUINT16 SensorPreviewResolutionX;
MUINT16 SensorPreviewResolutionY;
MUINT16 SensorFullResolutionX;
MUINT16 SensorFullResolutionY;
MUINT8 SensorClockFreq; /* MHz */
MUINT8 SensorCameraPreviewFrameRate;
MUINT8 SensorVideoFrameRate;
MUINT8 SensorStillCaptureFrameRate;
MUINT8 SensorWebCamCaptureFrameRate;
MUINT8 SensorClockPolarity; /*
SENSOR_CLOCK_POLARITY_HIGH/SENSOR_CLOCK_POLARITY_Low */
MUINT8 SensorClockFallingPolarity;
MUINT8 SensorClockRisingCount; /* 0..15 */
MUINT8 SensorClockFallingCount; /* 0..15 */
MUINT8 SensorClockDividCount; /* 0..15 */
MUINT8 SensorPixelClockCount; /* 0..15 */
MUINT8 SensorDataLatchCount; /* 0..15 */
MUINT8 SensorHsyncPolarity;
MUINT8 SensorVsyncPolarity;
MUINT8 SensorInterruptDelayLines;
MINT32 SensorResetActiveHigh;
MUINT32 SensorResetDelayCount;
ACDK_SENSOR_INTERFACE_TYPE_ENUM SensroInterfaceType;
ACDK_SENSOR_OUTPUT_DATA_FORMAT_ENUM SensorOutputDataFormat;
ACDK_SENSOR_MIPI_LANE_NUMBER_ENUM SensorMIPILaneNumber;
CAMERA_ISO_BINNING_INFO_STRUCT SensorISOBinningInfo;
MUINT32 CaptureDelayFrame;
MUINT32 PreviewDelayFrame;
MUINT32 VideoDelayFrame;
MUINT16 SensorGrabStartX;
MUINT16 SensorGrabStartY;
MUINT16 SensorDrivingCurrent;
MUINT8 SensorMasterClockSwitch;
MUINT8 AEShutDelayFrame; /* The frame of setting shutter default 0 for TG
int */
MUINT8 AESensorGainDelayFrame; /* The frame of setting sensor gain */
MUINT8 AEISPGainDelayFrame;
MUINT8 MIPIDataLowPwr2HighSpeedTermDelayCount;
MUINT8 MIPIDataLowPwr2HighSpeedSettleDelayCount;
MUINT8 MIPICLKLowPwr2HighSpeedTermDelayCount;
MUINT8 SensorWidthSampling;
MUINT8 SensorHightSampling;
MUINT8 SensorPacketECCOrder;
MUINT8 SensorDriver3D;
} ACDK_SENSOR_INFO_STRUCT, *PACDK_SENSOR_INFO_STRUCT;
这个结构体列取了 Sensor 的时钟频率、预览时的帧率、行同步/帧同步频率等参数。
第三个参数*pSensorConfigData 同样根据 MSDK_SENSOR_CONFIG_STRUCT 结构体
#define MSDK_SENSOR_CONFIG_STRUCT ACDK_SENSOR_CONFIG_STRUCT typedef struct
{
ACDK_SENSOR_IMAGE_MIRROR_ENUM SensorImageMirror;
MINT32 EnableShutterTansfer; /* Capture 时的快门设置 */
MINT32 EnableFlashlightTansfer; /*有闪光灯的 SensorCapture 时的快门设置*/
ACDK_SENSOR_OPERATION_MODE_ENUMSensorOperationMode;
MUINT16 ImageTargetWidth; /* Capture 的图像宽度 */
MUINT16 ImageTargetHeight; /* Capture 的图像高度*/
MUINT16 CaptureShutter; /* Capture 时的快门设置 */
MUINT16 FlashlightDuty; /*有闪光灯的 SensorCapture 时的快门设置*/
MUINT16 FlashlightOffset; /*有闪光灯的 SensorCapture 时的快门设置*/
MUINT16 FlashlightShutFactor; /*有闪光灯的 SensorCapture 时的快门设置*/
MUINT16 FlashlightMinShutter; /*有闪光灯的 SensorCapture 时的快门设置*/
ACDK_CAMERA_OPERATION_MODE_ENUMMetaMode;
MUINT32 DefaultPclk; // Sensor 默认的像素时钟频率(Ex:24000000)
MUINT32 Pixels;
MUINT32 Lines;
MUINT32 Shutter;
MUINT32 FrameLines;
} ACDK_SENSOR_CONFIG_STRUCT;
c)OV5647GetResolution
UINT32 OV5647GetResolution(MSDK_SENSOR_RESOLUTION_INFO_STRUCT *pSensorResolution) 此函数只有一个参数*pSensorResolution,
找到结构体:MSDK_SENSOR_RESOLUTION_INFO_STRUCT
#define MSDK_SENSOR_RESOLUTION_INFO_STRUCT ACDK_SENSOR_RESOLUTION_INFO_STRUCT
typedef struct
{
MUINT16 SensorPreviewWidth; //预览时的图像宽度
MUINT16 SensorPreviewHeight; //预览时的图像高度
MUINT16 SensorFullWidth;
MUINT16 SensorFullHeight;
} ACDK_SENSOR_RESOLUTION_INFO_STRUCT, *PACDK_SENSOR_RESOLUTION_INFO_STRUCT;
到这里可以发现同样是获取 Sensor 的信息,GetInfo 函数获取并设置了 Sensor 所处的模式、设置好需要的各种时钟、快门和拍照获取图像的信息整个流程的参数,
而 GetResolution 函数却是设置图像在预览模式下的参数,实际上是通过对实际捕获的图像缩放来提高预览时图像的解析度。
d)OV5647FeatureControl
UINT32OV5647FeatureControl(MSDK_SENSOR_FEATURE_ENUM FeatureId,
UINT8 *pFeaturePara, UINT32 *pFeatureParaLen)
在 FeatureControl 这个函数中有三个参数:
#define MSDK_SENSOR_FEATURE_ENUM
ACDK_SENSOR_FEATURE_ENUM typedef enum
{
SENSOR_FEATURE_BEGIN = SENSOR_FEATURE_START,
SENSOR_FEATURE_GET_RESOLUTION,
SENSOR_FEATURE_GET_PERIOD,
SENSOR_FEATURE_GET_PIXEL_CLOCK_FREQ,
SENSOR_FEATURE_SET_ESHUTTER,
SENSOR_FEATURE_SET_NIGHTMODE,
SENSOR_FEATURE_SET_GAIN,
SENSOR_FEATURE_SET_FLASHLIGHT,
SENSOR_FEATURE_SET_ISP_MASTER_CLOCK_FREQ,
SENSOR_FEATURE_SET_REGISTER,
SENSOR_FEATURE_GET_REGISTER,
SENSOR_FEATURE_SET_CCT_REGISTER,
SENSOR_FEATURE_GET_CCT_REGISTER,
SENSOR_FEATURE_SET_ENG_REGISTER,
SENSOR_FEATURE_GET_ENG_REGISTER,
SENSOR_FEATURE_GET_REGISTER_DEFAULT,
SENSOR_FEATURE_GET_CONFIG_PARA,
SENSOR_FEATURE_CAMERA_PARA_TO_SENSOR,
SENSOR_FEATURE_SENSOR_TO_CAMERA_PARA, SENSOR_FEATURE_GET_GROUP_COUNT, SENSOR_FEATURE_GET_GROUP_INFO, SENSOR_FEATURE_GET_ITEM_INFO, SENSOR_FEATURE_SET_ITEM_INFO, SENSOR_FEATURE_GET_ENG_INFO, SENSOR_FEATURE_GET_LENS_DRIVER_ID, SENSOR_FEATURE_SET_YUV_CMD, SENSOR_FEATURE_SET_VIDEO_MODE, SENSOR_FEATURE_SET_CALIBRATION_DATA, SENSOR_FEATURE_SET_SENSOR_SYNC, SENSOR_FEATURE_INITIALIZE_AF, SENSOR_FEATURE_CONSTant_AF, SENSOR_FEATURE_MOVE_FOCUS_LENS, SENSOR_FEATURE_GET_AF_STATUS, SENSOR_FEATURE_GET_AF_INF, SENSOR_FEATURE_GET_AF_MACRO, SENSOR_FEATURE_CHECK_SENSOR_ID, SENSOR_FEATURE_SET_AUTO_FLICKER_MODE, SENSOR_FEATURE_SET_TEST_PATTERN, SENSOR_FEATURE_SET_SOFTWARE_PWDN, SENSOR_FEATURE_SINGLE_FOCUS_MODE, SENSOR_FEATURE_CANCEL_AF, SENSOR_FEATURE_SET_AF_WINDOW, SENSOR_FEATURE_GET_EV_AWB_REF, SENSOR_FEATURE_GET_SHUTTER_GAIN_AWB_GAIN, SENSOR_FEATURE_MAX
} ACDK_SENSOR_FEATURE_ENUM;
FeatureId 这个参数提供了低层给上层接口的准备。*pFeaturePara 和*pFeatureParaLen 分别是 FeatureId 的具体值。
a) 驱动的注册
在(\custom\common\kernel\imgsensor\src\Kd_sensorlist.c)CAMERA_HW_I2C_init 这个
static int __init CAMERA_HW_i2C_init(void)
{
struct proc_dir_entry *prEntry;
//i2c_register_board_info(CAMERA_I2C_BUSNUM, &kd_camera_dev, 1);
i2c_register_board_info(SUPPORT_I2C_BUS_NUM1, &i2c_devs1, 1);
//i2c_register_board_info(SUPPORT_I2C_BUS_NUM2, &i2c_devs2, 1);
if(platform_driver_register(&g_stCAMERA_HW_Driver)){
PK_ERR("failed to register CAMERA_HW driver\n");
return -ENODEV;
}
//if(platform_driver_register(&g_stCAMERA_HW_Driver2)){
// PK_ERR("failed to register CAMERA_HW driver\n");
// return -ENODEV;
//}
//Register proc file for main sensor register debug
prEntry = create_proc_entry("driver/camsensor", 0, NULL);
if (prEntry) {
prEntry->read_proc = CAMERA_HW_DumpReg_To_Proc;
prEntry->write_proc = CAMERA_HW_Reg_Debug;
}
else {
PK_ERR("add /proc/driver/camsensor entry fail \n");
}
//Register proc file for sub sensor register debug
prEntry = create_proc_entry("driver/camsensor2", 0, NULL);
if (prEntry) {
prEntry->read_proc = CAMERA_HW_DumpReg_To_Proc;
prEntry->write_proc = CAMERA_HW_Reg_Debug2;
}
else {
PK_ERR("add /proc/driver/camsensor2 entry fail \n");
}
atoMIC_set(&g_CamHWOpend, 0);
//atomic_set(&g_CamHWOpend2, 0);
atomic_set(&g_CamDrvOpenCnt, 0);
//atomic_set(&g_CamDrvOpenCnt2, 0);
atomic_set(&g_CamHWOpening, 0);
return 0;
}
函数里通过 Platform_driver_register(&g_stCAMERA_HW_Driver)把 Camera 模块驱动注册 到 Platform 总线上。
而 g_stCAMERA_HW_Driver 是对结构体 Platform_driver 这个结构体的填充。
///////////////////////////////// i2c_register_board_info 的解释
//i2c_board_info用于构建信息表来列出存在的I2C设备。这一信息用于增长新型I2C驱动的驱动模型树。对于主板,它使用i2c_register_board_info()来静态创建。对于子板,利用已知的适配器使用i2c_new_device()动态创建。
//I2C 设备创建模板
struct i2c_board_info {
char type[I2C_NAME_SIZE]; //芯片类型,用于初始化i2c_CLIent.name
unsigned short flags; //用于初始化i2c_client.flags
unsigned short addr; //存储于i2c_client.addr
void *platform_data; //存储于i2c_client.dev.platform_data
struct dev_archdata *archdata; //拷贝至i2c_client.dev.archdata
int irq; //存储于i2c_client.irq
};
//使用linux I2C驱动栈,系统可以在初始化时宣告板载信息表。这些应该在靠近arch_initcall()时的板子相关的初始化代码或同等情况时,在I2C适配器驱动被注册之前被执行。例如,主板初始化代码可以定义//几个设备,也可以在叠板的每个子板初始化代码中定义。
//I2C设备会在相关的总线适配器被注册后创建。此后,标准驱动模型工具通常绑定新型I2C驱动至I2C设备。对于使用这一函数宣告的设备,在动态分配的情况下总线号是不可用的。
//传递的板子信息可以安全的是__initdata,但是由于不能拷贝,要小心嵌入式指针(如platform_data,functions等)
//静态的宣告I2C设备
int i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len);
@busnum: 指定这些设备属于哪个总线
@info: I2C设备描述符向量
@len: 向量中描述符的数量;为了预留特定的总线号,可以是0。
在你对应的machine配置里会执行“i2c_register_board_info”一个函数,它会将一个i2c_board_info的结构体注册进系统,
可以发现,在目录/sys/bus/i2c/devices下的设备就是这个i2c_board_info结构体里所描述的I2C设备,
而/sys/bus/i2c/devices下的设备名字就是根据i2c_board_info结构体中定义的I2C Address来命名的。
所以添加一个I2C设备时,除了需要编写这个I2C设备的驱动之外,还需要在machine里面加入I2C设备的i2c_board_info内容。
/////////////////////////////////////////////////////////////////////
///////////////////////////////platform_driver_register 的解释
platform_driver_register()
是来注册设备的驱动程序
platform_device_register()
是来注册设备硬件,告诉kernel,当前有什么设备
当某个设备connect后,必须在device和driver的两条线上都有匹配的东西后,才算是匹配上,并且开始使用driver的probe等函数进行硬件初始化工作。
////////////////////////
// (Kernel\include\linux\Platform_device.h)
static struct platform_driver g_stCAMERA_HW_Driver = {
.probe = CAMERA_HW_probe,
.remove = CAMERA_HW_remove,
.suspend = CAMERA_HW_suspend,
.resume = CAMERA_HW_resume,
.driver = {
.name = "image_sensor",
.owner = THIS_MODULE,
}
};
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
static int CAMERA_HW_probe(struct platform_device *pdev)
{
return i2c_add_driver(&CAMERA_HW_i2c_driver);
}
static int CAMERA_HW_remove(struct platform_device *pdev)
{
i2c_del_driver(&CAMERA_HW_i2c_driver);
return 0;
}
static int CAMERA_HW_suspend(struct platform_device *pdev, pm_message_t mesg)
{
return 0;
}
static int CAMERA_HW_resume(struct platform_device *pdev)
{
return 0;
}
b) 设备的注册:
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
};
//kernel\arch\sh\boards\mach-ap325rxa\Setup.c
static struct platform_device ap325rxa_camera[] = {
{
.name = "soc-camera-pdrv",
.id = 0,
.dev = {
.platform_data = &ov7725_link,
},
}, {
.name = "soc-camera-pdrv",
.id = 1,
.dev = {
.platform_data = &camera_link,
},
},
};
static struct soc_camera_link camera_link = {
.bus_id = 0,
.add_device = ap325rxa_camera_add,
.del_device = ap325rxa_camera_del,
.module_name = "soc_camera_platform",
.priv = &camera_info,
};
static int ap325rxa_camera_add(struct soc_camera_device *icd)
{
int ret = soc_camera_platform_add(icd, &camera_device, &camera_link,
ap325rxa_camera_release, 0);
if (ret < 0)
return ret;
ret = camera_probe();
if (ret < 0)
soc_camera_platform_del(icd, camera_device, &camera_link);
return ret;
}
static void ap325rxa_camera_del(struct soc_camera_device *icd)
{
soc_camera_platform_del(icd, camera_device, &camera_link);
}
static struct soc_camera_platform_info camera_info = {
.format_name = "UYVY",
.format_depth = 16,
.format = {
.code = V4L2_MBUS_FMT_UYVY8_2X8,
.colorspace = V4L2_COLORSPACE_SMPTE170M,
.field = V4L2_FIELD_NONE,
.width = 640,
.height = 480,
},
.mbus_param = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER |
V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH |
V4L2_MBUS_DATA_ACTIVE_HIGH,
.mbus_type = V4L2_MBUS_PARALLEL,
.set_capture = camera_set_capture,
};
c) 总线的匹配:
既 然 是 驱 动 Platform_device 那 对 应 的 设 备 必 然 是 挂 载 Platform 总 线 上 的Platform_device,Platform 总线是 Linux 系统提供的一种机制,不同于 I2C、I2S 等总线,它 是一种虚拟的总线。
Linux 系统为 Platform 总线定义了一个 bus_type 的实例 Platform_bus_type:
//(Kernel\drivers\base\platform.c)
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup();
error = device_register(&platform_bus);
if (error)
return error;
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
return error;
}
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* Attempt an OF style match first */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try to match against the id table */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
Platform 总线通过 platform_match 这个成员函数来确定 platform_device 与 platform_driver 如何进行匹配:
static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct platform_device *pdev = to_platform_device(dev);
int rc;
/* Some devices have extra OF data and an OF-style MODALIAS */
rc = of_device_uevent_modalias(dev,env);
if (rc != -ENODEV)
return rc;
add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX,
pdev->name);
return 0;
}
static const struct dev_pm_ops platform_dev_pm_ops = {
.runtime_suspend = pm_generic_runtime_suspend,
.runtime_resume = pm_generic_runtime_resume,
.runtime_idle = pm_generic_runtime_idle,
USE_PLATFORM_PM_SLEEP_OPS
};
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
arch_setup_pdev_archdata(pdev);
return platform_device_add(pdev);
}
void platform_device_unregister(struct platform_device *pdev)
{
platform_device_del(pdev);
platform_device_put(pdev);
}
从上图可以清晰的了解到 Camera 的一个工作流程主要分为这么七步:
1.打开 Camera power LDO,让 Camera 有能量保证。
2.打开 IIC,设置 PDN 引脚,使 Camera 退出出 Standby 模式,按照要求让 Reset 脚 做一个复位动作。
3.读一下 sensor 的版本 ID,这样可以让你确认是否连接上你想要的 sensor。
4.对 Sensor 进行初始化下载最基本的参数让 Sensor 工作起来,可能包括软复位。
5.下载 preview 的参数,为预览动作准备。
6.下载 Capture 的参数,为拍照动作准备。
7.设置 PDN 引脚,使 Sensor 进入 Standby 模式,或者关掉 LDO 等动作,退出 Camera。
d)设备驱动
我们都知道,Linux 内核是通过模块的机制来加载设备驱动的,那么接下来我们就从设备模块加载的角度来看下 Camera 工作流程的驱动代码是如何工作的。
在-alps\mediatek\custom\common\kernel\imgsensor\src\kd_sensorlist.c 中可以看到:
module_init(CAMERA_HW_i2C_init);
module_exit(CAMERA_HW_i2C_exit);
在这里 Linux 内核加载和卸载 Camera 模块。
struct i2c_driver CAMERA_HW_i2c_driver = {
.probe = CAMERA_HW_i2c_probe,
.remove = CAMERA_HW_i2c_remove,
.driver.name = CAMERA_HW_DRVNAME1,
.id_table = CAMERA_HW_i2c_id,
};
static int CAMERA_HW_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int i4RetValue = 0;
PK_DBG("[CAMERA_HW] Attach I2C \n");
//get sensor i2c client
SPIn_lock(&kdsensor_drv_lock);
g_pstI2Cclient = client;
//set I2C clock rate
g_pstI2Cclient->timing = 300;//200k
spin_unlock(&kdsensor_drv_lock);
//Register char driver
i4RetValue = RegisterCAMERA_HWCharDrv();
if(i4RetValue){
PK_ERR("[CAMERA_HW] register char device failed!\n");
return i4RetValue;
}
//spin_lock_init(&g_CamHWLock);
PK_DBG("[CAMERA_HW] Attached! \n");
return 0;
}
static int CAMERA_HW_i2c_remove(struct i2c_client *client)
{
return 0;
}
static const struct i2c_device_id CAMERA_HW_i2c_id[] = {{CAMERA_HW_DRVNAME1,0},{}};
inline static int RegisterCAMERA_HWCharDrv(void)
{
struct device* sensor_device = NULL;
#if CAMERA_HW_DYNAMIC_ALLOCATE_DEVNO
if( alloc_chrdev_region(&g_CAMERA_HWdevno, 0, 1,CAMERA_HW_DRVNAME1) )
{
PK_DBG("[CAMERA SENSOR] Allocate device no failed\n");
return -EAGAIN;
}
#else
if( register_chrdev_region( g_CAMERA_HWdevno , 1 , CAMERA_HW_DRVNAME1) )
{
PK_DBG("[CAMERA SENSOR] Register device no failed\n");
return -EAGAIN;
}
#endif
//Allocate driver
g_pCAMERA_HW_CharDrv = cdev_alloc();
if(NULL == g_pCAMERA_HW_CharDrv)
{
unregister_chrdev_region(g_CAMERA_HWdevno, 1);
PK_DBG("[CAMERA SENSOR] Allocate mem for kobject failed\n");
return -ENOMEM;
}
//Attatch file operation.
cdev_init(g_pCAMERA_HW_CharDrv, &g_stCAMERA_HW_fops);
g_pCAMERA_HW_CharDrv->owner = THIS_MODULE;
//Add to system
if(cdev_add(g_pCAMERA_HW_CharDrv, g_CAMERA_HWdevno, 1))
{
PK_DBG("[MT6516_IDP] Attatch file operation failed\n");
unregister_chrdev_region(g_CAMERA_HWdevno, 1);
return -EAGAIN;
}
sensor_class = class_create(THIS_MODULE, "sensordrv");
if (IS_ERR(sensor_class)) {
int ret = PTR_ERR(sensor_class);
PK_DBG("Unable to create class, err = %d\n", ret);
return ret;
}
sensor_device = device_create(sensor_class, NULL, g_CAMERA_HWdevno, NULL, CAMERA_HW_DRVNAME1);
return 0;
}
在 RegisterCAMERA_HWCharDrv()中cdev_init(g_pCAMERA_HW_CharDrv, &g_stCAMERA_HW_fops);
对设备进行初始化,并将g_stCAMERA_HW_fops 这个文件操作函数作为上层对 Camera 设备操作的接口留给上层进 行调用:
static const struct file_operations g_stCAMERA_HW_fops =
{
.owner = THIS_MODULE,
.open = CAMERA_HW_Open,
.release = CAMERA_HW_Release,
.unlocked_ioctl = CAMERA_HW_Ioctl
};
其中成员函数 open()只是初始化一个原子变量留给系统调用。
ioctl()才是整个 Camera驱动的入口:CAMERA_HW_Ioctl()是上层文件操作系统操作底层硬件的方法,它先对 Camera 需要的Buffer 做一个初始化,然后建立对 Cameraopen、getinfo 等操作的接口:
static long CAMERA_HW_Ioctl(
struct file * a_pstFile,
unsigned int a_u4Command,
unsigned long a_u4Param
)
{
int i4RetValue = 0;
void * pBuff = NULL;
u32 *pIdx = NULL;
mutex_lock(&kdCam_Mutex);
if(_IOC_NONE == _IOC_DIR(a_u4Command)) {
}
else {
pBuff = kmalloc(_IOC_SIZE(a_u4Command),GFP_KERNEL);
if(NULL == pBuff) {
PK_DBG("[CAMERA SENSOR] ioctl allocate mem failed\n");
i4RetValue = -ENOMEM;
goto CAMERA_HW_Ioctl_EXIT;
}
if(_IOC_WRITE & _IOC_DIR(a_u4Command)){
if(copy_fROM_user(pBuff , (void *) a_u4Param, _IOC_SIZE(a_u4Command))) {
kfree(pBuff);
PK_DBG("[CAMERA SENSOR] ioctl copy from user failed\n");
i4RetValue = -EFAULT;
goto CAMERA_HW_Ioctl_EXIT;
}
}
}
pIdx = (u32*)pBuff;
switch(a_u4Command) {
#if 0
case KDIMGSENSORIOC_X_POWER_ON:
i4RetValue = kdModulePowerOn((CAMERA_DUAL_CAMERA_SENSOR_ENUM) *pIdx, true, CAMERA_HW_DRVNAME);
break;
case KDIMGSENSORIOC_X_POWER_OFF:
i4RetValue = kdModulePowerOn((CAMERA_DUAL_CAMERA_SENSOR_ENUM) *pIdx, false, CAMERA_HW_DRVNAME);
break;
#endif
case KDIMGSENSORIOC_X_SET_DRIVER:
i4RetValue = kdSetDriver((unsigned int*)pBuff);
//在 KdSetDriver()中通过判断 name 和 ID 匹配具体型号的 sensor 的驱动,判断它是主摄还 是次摄,并对它进行初始化:
break;
case KDIMGSENSORIOC_T_OPEN:
i4RetValue = adopt_CAMERA_HW_Open();
break;
case KDIMGSENSORIOC_X_GETINFO:
i4RetValue = adopt_CAMERA_HW_GetInfo(pBuff);
break;
case KDIMGSENSORIOC_X_GETRESOLUTION:
i4RetValue = adopt_CAMERA_HW_GetResolution(pBuff);
break;
case KDIMGSENSORIOC_X_FEATURECONCTROL:
i4RetValue = adopt_CAMERA_HW_FeatureControl(pBuff);
break;
case KDIMGSENSORIOC_X_CONTROL:
i4RetValue = adopt_CAMERA_HW_Control(pBuff);
break;
case KDIMGSENSORIOC_T_CLOSE:
i4RetValue = adopt_CAMERA_HW_Close();
break;
case KDIMGSENSORIOC_T_CHECK_IS_ALIVE:
i4RetValue = adopt_CAMERA_HW_CheckIsAlive();
break;
case KDIMGSENSORIOC_X_GET_SOCKET_POS:
i4RetValue = kdGetSocketPostion((unsigned int*)pBuff);
break;
case KDIMGSENSORIOC_X_SET_I2CBUS:
//i4RetValue = kdSetI2CBusNum(*pIdx);
break;
case KDIMGSENSORIOC_X_RELEASE_I2C_TRIGGER_LOCK:
//i4RetValue = kdReleaseI2CTriggerLock();
break;
default :
PK_DBG("No such command \n");
i4RetValue = -EPERM;
break;
}
if(_IOC_READ & _IOC_DIR(a_u4Command)) {
if(copy_to_user((void __user *) a_u4Param , pBuff , _IOC_SIZE(a_u4Command))) {
kfree(pBuff);
PK_DBG("[CAMERA SENSOR] ioctl copy to user failed\n");
i4RetValue = -EFAULT;
goto CAMERA_HW_Ioctl_EXIT;
}
}
kfree(pBuff);
CAMERA_HW_Ioctl_EXIT:
mutex_unlock(&kdCam_Mutex);
return i4RetValue;
}
通过判断 Sensor 状态的逻辑值来进行具体的操作,对于这个值的定义在:
Mediatek\custom\common\kernel\imgsensor\inc\Kd_imgsensor.h 中
//sensorOpen
//This command will TBD
#define KDIMGSENSORIOC_T_OPEN _IO(IMGSENSORMAGIC,0)
//sensorGetInfo
//This command will TBD
#define KDIMGSENSORIOC_X_GETINFO _IOWR(IMGSENSORMAGIC,5,ACDK_SENSOR_GETINFO_STRUCT)
//sensorGetResolution
//This command will TBD
#define KDIMGSENSORIOC_X_GETRESOLUTION _IOWR(IMGSENSORMAGIC,10,ACDK_SENSOR_RESOLUTION_INFO_STRUCT)
//sensorFeatureControl
//This command will TBD
#define KDIMGSENSORIOC_X_FEATURECONCTROL _IOWR(IMGSENSORMAGIC,15,ACDK_SENSOR_FEATURECONTROL_STRUCT)
//sensorControl
//This command will TBD
#define KDIMGSENSORIOC_X_CONTROL _IOWR(IMGSENSORMAGIC,20,ACDK_SENSOR_CONTROL_STRUCT)
//sensorClose
//This command will TBD
#define KDIMGSENSORIOC_T_CLOSE _IO(IMGSENSORMAGIC,25)
//sensorSearch
#define KDIMGSENSORIOC_T_CHECK_IS_ALIVE _IO(IMGSENSORMAGIC, 30)
//set sensor driver
#define KDIMGSENSORIOC_X_SET_DRIVER _IOWR(IMGSENSORMAGIC,35,SENSOR_DRIVER_INDEX_STRUCT)
//get socket postion
#define KDIMGSENSORIOC_X_GET_SOCKET_POS _IOWR(IMGSENSORMAGIC,40,u32)
//set I2C bus
#define KDIMGSENSORIOC_X_SET_I2CBUS _IOWR(IMGSENSORMAGIC,45,u32)
//set I2C bus
#define KDIMGSENSORIOC_X_RELEASE_I2C_TRIGGER_LOCK _IO(IMGSENSORMAGIC,50)
//Set Shutter Gain Wait Done
#define KDIMGSENSORIOC_X_SET_SHUTTER_GAIN_WAIT_DONE _IOWR(IMGSENSORMAGIC,55,u32)//HDR
//set mclk
#define KDIMGSENSORIOC_X_SET_MCLK_PLL _IOWR(IMGSENSORMAGIC,60,ACDK_SENSOR_MCLK_STRUCT)
在 KdSetDriver()中通过判断 name 和 ID 匹配具体型号的 sensor 的驱动,判断它是主摄还 是次摄,并对它进行初始化:
if ( MAX_NUM_OF_SUPPORT_SENSOR > drvIdx ) {
if (NULL == pSensorList[drvIdx].SensorInit) {
PK_ERR("ERROR:kdSetDriver()\n");
return -EIO;
}
pSensorList[drvIdx].SensorInit(&g_pInvokeSensorFunc);
if (NULL == g_pInvokeSensorFunc) {
PK_ERR("ERROR:NULL g_pSensorFunc[%d]\n",i);
return -EIO;
}
通过 NAME 和 ID 匹配完成后会将 PSENSOR_FUNCTION_STRUCT *pfFunc 这个结构体匹 配到具体型号的驱动代码中:
SENSOR_FUNCTION_STRUCT SensorFuncOV5648mipi=
{
OV5648MIPIOpen,
OV5648MIPIGetInfo,
OV5648MIPIGetResolution,
OV5648MIPIFeatureControl,
OV5648MIPIControl,
OV5648MIPIClose
};
到这里,整个 Camera 驱动从总线注册到完成具体 sensor 的初始化的流程就完成了,CAMERA_HW_Ioctl()中其他的 ioctl 操作函数最后都会在$sensor$_sensor.c 中实现。
1、 修改系统配置文件 ProjectConfig.mk:
-alps\mediatek\config\$project$\ProjectConfig.mk
2、 检查、配置供电文件:
-alps\mediatek\custom\$project$\Kernel\Camera\Camera\kd_camera_hw.c
其实在 kd_camera_hw.c 中只有一个函数 kdCISModulePowerOn(),在这个 函数中需要注意的是通过 GPIO 口控制 PDN 和 RST 引脚的时候,对于其相关的
定义,由其在切换平台的时候。例如 MT6573 和 MT6575 的定义顺序就不同。
b)OV5647GetInfo
UINT32 OV5647GetInfo(MSDK_SCENARIO_ID_ENUM ScenarioId,
MSDK_SENSOR_INFO_STRUCT *pSensorInfo,
MSDK_SENSOR_CONFIG_STRUCT *pSensorConfigData)
第一个参数 ScenarioId 来自于 MSDK_SCENARIO_ID_ENUM 这个数组,在kd_imgsensor_define.h 中是这样定义的:
#define MSDK_SCENARIO_ID_ENUM ACDK_SCENARIO_ID_ENUM
typedef enum
{
ACDK_SCENARIO_ID_CAMERA_PREVIEW=0,
ACDK_SCENARIO_ID_VIDEO_PREVIEW,
ACDK_SCENARIO_ID_VIDEO_CAPTURE_MPEG4,
ACDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG,
ACDK_SCENARIO_ID_CAMERA_CAPTURE_MEM,
ACDK_SCENARIO_ID_CAMERA_BURST_CAPTURE_JPEG,
ACDK_SCENARIO_ID_VIDEO_DECODE_MPEG4,
ACDK_SCENARIO_ID_VIDEO_DECODE_H263,
ACDK_SCENARIO_ID_VIDEO_DECODE_H264,
ACDK_SCENARIO_ID_VIDEO_DECODE_WMV78,
ACDK_SCENARIO_ID_VIDEO_DECODE_WMV9,
ACDK_SCENARIO_ID_VIDEO_DECODE_MPEG2,
ACDK_SCENARIO_ID_IMAGE_YUV2RGB,
ACDK_SCENARIO_ID_IMAGE_RESIZE,
ACDK_SCENARIO_ID_IMAGE_ROTATE,
ACDK_SCENARIO_ID_IMAGE_POST_PROCESS, ACDK_SCENARIO_ID_JPEG_RESIZE, ACDK_SCENARIO_ID_JPEG_DECODE, ACDK_SCENARIO_ID_JPEG_PARSE, ACDK_SCENARIO_ID_JPEG_ENCODE, ACDK_SCENARIO_ID_JPEG_ENCODE_THUMBNAIL, ACDK_SCENARIO_ID_DRIVER_IO_CONTROL, ACDK_SCENARIO_ID_DO_NOT_CARE, ACDK_SCENARIO_ID_IMAGE_DSPL_BUFFER_ALLOC, ACDK_SCENARIO_ID_TV_OUT, ACDK_SCENARIO_ID_MAX,
ACDK_SCENARIO_ID_VIDOE_ENCODE_WITHOUT_PREVIEW, ACDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG_BACK_PREVIEW, ACDK_SCENARIO_ID_VIDEO_DECODE_RV8, ACDK_SCENARIO_ID_VIDEO_DECODE_RV9, ACDK_SCENARIO_ID_CAMERA_ZSD,
} ACDK_SCENARIO_ID_ENUM;
通过这个数组定义 Camera 的各种模式,并且给他们从 0 开始给一个模拟的 ID,通过这个
ScenarioID 来控制 Camera 的工作模式是在拍照、摄像等等。
想要了解*pSensorInfo 这个指针的内容就得看 MSDK_SENSOR_INFO_STRUCT 的定义
#define MSDK_SENSOR_INFO_STRUCT ACDK_SENSOR_INFO_STRUCT typedef struct
{
MUINT16 SensorPreviewResolutionX;
MUINT16 SensorPreviewResolutionY;
MUINT16 SensorFullResolutionX;
MUINT16 SensorFullResolutionY;
MUINT8 SensorClockFreq; /* MHz */
MUINT8 SensorCameraPreviewFrameRate;
MUINT8 SensorVideoFrameRate;
MUINT8 SensorStillCaptureFrameRate;
MUINT8 SensorWebCamCaptureFrameRate;
MUINT8 SensorClockPolarity; /*
SENSOR_CLOCK_POLARITY_HIGH/SENSOR_CLOCK_POLARITY_Low */
MUINT8 SensorClockFallingPolarity;
MUINT8 SensorClockRisingCount; /* 0..15 */
MUINT8 SensorClockFallingCount; /* 0..15 */
MUINT8 SensorClockDividCount; /* 0..15 */
MUINT8 SensorPixelClockCount; /* 0..15 */
MUINT8 SensorDataLatchCount; /* 0..15 */
MUINT8 SensorHsyncPolarity;
MUINT8 SensorVsyncPolarity;
MUINT8 SensorInterruptDelayLines;
MINT32 SensorResetActiveHigh;
MUINT32 SensorResetDelayCount;
ACDK_SENSOR_INTERFACE_TYPE_ENUM SensroInterfaceType;
ACDK_SENSOR_OUTPUT_DATA_FORMAT_ENUM SensorOutputDataFormat;
ACDK_SENSOR_MIPI_LANE_NUMBER_ENUM SensorMIPILaneNumber;
CAMERA_ISO_BINNING_INFO_STRUCT SensorISOBinningInfo;
MUINT32 CaptureDelayFrame;
MUINT32 PreviewDelayFrame;
MUINT32 VideoDelayFrame;
MUINT16 SensorGrabStartX;
MUINT16 SensorGrabStartY;
MUINT16 SensorDrivingCurrent;
MUINT8 SensorMasterClockSwitch;
MUINT8 AEShutDelayFrame; /* The frame of setting shutter default 0 for TG
int */
MUINT8 AESensorGainDelayFrame; /* The frame of setting sensor gain */
MUINT8 AEISPGainDelayFrame;
MUINT8 MIPIDataLowPwr2HighSpeedTermDelayCount;
MUINT8 MIPIDataLowPwr2HighSpeedSettleDelayCount;
MUINT8 MIPICLKLowPwr2HighSpeedTermDelayCount;
MUINT8 SensorWidthSampling;
MUINT8 SensorHightSampling;
MUINT8 SensorPacketECCOrder;
MUINT8 SensorDriver3D;
} ACDK_SENSOR_INFO_STRUCT, *PACDK_SENSOR_INFO_STRUCT;
这个结构体列取了 Sensor 的时钟频率、预览时的帧率、行同步/帧同步频率等参数。
第三个参数*pSensorConfigData 同样根据 MSDK_SENSOR_CONFIG_STRUCT 结构体
#define MSDK_SENSOR_CONFIG_STRUCT ACDK_SENSOR_CONFIG_STRUCT typedef struct
{
ACDK_SENSOR_IMAGE_MIRROR_ENUM SensorImageMirror;
MINT32 EnableShutterTansfer; /* Capture 时的快门设置 */
MINT32 EnableFlashlightTansfer; /*有闪光灯的 SensorCapture 时的快门设置*/
ACDK_SENSOR_OPERATION_MODE_ENUMSensorOperationMode;
MUINT16 ImageTargetWidth; /* Capture 的图像宽度 */
MUINT16 ImageTargetHeight; /* Capture 的图像高度*/
MUINT16 CaptureShutter; /* Capture 时的快门设置 */
MUINT16 FlashlightDuty; /*有闪光灯的 SensorCapture 时的快门设置*/
MUINT16 FlashlightOffset; /*有闪光灯的 SensorCapture 时的快门设置*/
MUINT16 FlashlightShutFactor; /*有闪光灯的 SensorCapture 时的快门设置*/
MUINT16 FlashlightMinShutter; /*有闪光灯的 SensorCapture 时的快门设置*/
ACDK_CAMERA_OPERATION_MODE_ENUMMetaMode;
MUINT32 DefaultPclk; // Sensor 默认的像素时钟频率(Ex:24000000)
MUINT32 Pixels;
MUINT32 Lines;
MUINT32 Shutter;
MUINT32 FrameLines;
} ACDK_SENSOR_CONFIG_STRUCT;
c)OV5647GetResolution
UINT32 OV5647GetResolution(MSDK_SENSOR_RESOLUTION_INFO_STRUCT *pSensorResolution) 此函数只有一个参数*pSensorResolution,
找到结构体:MSDK_SENSOR_RESOLUTION_INFO_STRUCT
#define MSDK_SENSOR_RESOLUTION_INFO_STRUCT ACDK_SENSOR_RESOLUTION_INFO_STRUCT
typedef struct
{
MUINT16 SensorPreviewWidth; //预览时的图像宽度
MUINT16 SensorPreviewHeight; //预览时的图像高度
MUINT16 SensorFullWidth;
MUINT16 SensorFullHeight;
} ACDK_SENSOR_RESOLUTION_INFO_STRUCT, *PACDK_SENSOR_RESOLUTION_INFO_STRUCT;
到这里可以发现同样是获取 Sensor 的信息,GetInfo 函数获取并设置了 Sensor 所处的模式、设置好需要的各种时钟、快门和拍照获取图像的信息整个流程的参数,
而 GetResolution 函数却是设置图像在预览模式下的参数,实际上是通过对实际捕获的图像缩放来提高预览时图像的解析度。
d)OV5647FeatureControl
UINT32OV5647FeatureControl(MSDK_SENSOR_FEATURE_ENUM FeatureId,
UINT8 *pFeaturePara, UINT32 *pFeatureParaLen)
在 FeatureControl 这个函数中有三个参数:
#define MSDK_SENSOR_FEATURE_ENUM
ACDK_SENSOR_FEATURE_ENUM typedef enum
{
SENSOR_FEATURE_BEGIN = SENSOR_FEATURE_START,
SENSOR_FEATURE_GET_RESOLUTION,
SENSOR_FEATURE_GET_PERIOD,
SENSOR_FEATURE_GET_PIXEL_CLOCK_FREQ,
SENSOR_FEATURE_SET_ESHUTTER,
SENSOR_FEATURE_SET_NIGHTMODE,
SENSOR_FEATURE_SET_GAIN,
SENSOR_FEATURE_SET_FLASHLIGHT,
SENSOR_FEATURE_SET_ISP_MASTER_CLOCK_FREQ,
SENSOR_FEATURE_SET_REGISTER,
SENSOR_FEATURE_GET_REGISTER,
SENSOR_FEATURE_SET_CCT_REGISTER,
SENSOR_FEATURE_GET_CCT_REGISTER,
SENSOR_FEATURE_SET_ENG_REGISTER,
SENSOR_FEATURE_GET_ENG_REGISTER,
SENSOR_FEATURE_GET_REGISTER_DEFAULT,
SENSOR_FEATURE_GET_CONFIG_PARA,
SENSOR_FEATURE_CAMERA_PARA_TO_SENSOR,
SENSOR_FEATURE_SENSOR_TO_CAMERA_PARA, SENSOR_FEATURE_GET_GROUP_COUNT, SENSOR_FEATURE_GET_GROUP_INFO, SENSOR_FEATURE_GET_ITEM_INFO, SENSOR_FEATURE_SET_ITEM_INFO, SENSOR_FEATURE_GET_ENG_INFO, SENSOR_FEATURE_GET_LENS_DRIVER_ID, SENSOR_FEATURE_SET_YUV_CMD, SENSOR_FEATURE_SET_VIDEO_MODE, SENSOR_FEATURE_SET_CALIBRATION_DATA, SENSOR_FEATURE_SET_SENSOR_SYNC, SENSOR_FEATURE_INITIALIZE_AF, SENSOR_FEATURE_CONSTant_AF, SENSOR_FEATURE_MOVE_FOCUS_LENS, SENSOR_FEATURE_GET_AF_STATUS, SENSOR_FEATURE_GET_AF_INF, SENSOR_FEATURE_GET_AF_MACRO, SENSOR_FEATURE_CHECK_SENSOR_ID, SENSOR_FEATURE_SET_AUTO_FLICKER_MODE, SENSOR_FEATURE_SET_TEST_PATTERN, SENSOR_FEATURE_SET_SOFTWARE_PWDN, SENSOR_FEATURE_SINGLE_FOCUS_MODE, SENSOR_FEATURE_CANCEL_AF, SENSOR_FEATURE_SET_AF_WINDOW, SENSOR_FEATURE_GET_EV_AWB_REF, SENSOR_FEATURE_GET_SHUTTER_GAIN_AWB_GAIN, SENSOR_FEATURE_MAX
} ACDK_SENSOR_FEATURE_ENUM;
FeatureId 这个参数提供了低层给上层接口的准备。*pFeaturePara 和*pFeatureParaLen 分别是 FeatureId 的具体值。
请教一下小编,怎么和Linux的v4l2挂上的呢?
TKS for share
_________________NB
NB