概述
camera的开机上电分两个流程,一个是内核中camera驱动进行挂载,另一个是用户空间通过ioctrl对内核空间camera驱动进行上电。
驱动挂载:
1.module_init(imgsensor_init); module_init用来进行驱动的加载,传入函数指针imgsensor_init调用imgsensor_init()
2.platform_driver_register(&gimgsensor_platform_driver); 注册平台驱动gimgsensor_platform_driver,gimgsensor_platform_driver内部有一个节点.of_match_table:compatible = "mediatek,imgsensor",这里CONFIG_OF成立,将采用设备树的方式进行匹配,对应kd_camera_hw1,匹配到后就会进行probe操作
static struct platform_driver gimgsensor_platform_driver = {
.probe = imgsensor_probe,
.remove = imgsensor_remove,
.suspend = imgsensor_suspend,
.resume = imgsensor_resume,
.driver = {
.name = "image_sensor",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = gimgsensor_of_device_id,
#endif
}
};
kd_camera_hw1:kd_camera_hw1@1a004000 {
compatible = "mediatek,imgsensor";
};
3.imgsensor_probe(struct platform_device *pplatform_device)
3.1.struct IMGSENSOR *pimgsensor = &gimgsensor; 创建IMGSENSOR资源
struct IMGSENSOR {
dev_t dev_no;
struct cdev *pcdev;
struct class *pclass;
struct IMGSENSOR_STATUS status;
struct IMGSENSOR_HW hw;
struct IMGSENSOR_SENSOR sensor[IMGSENSOR_SENSOR_IDX_MAX_NUM];
struct IMGSENSOR_SENSOR_LIST *psensor_list[MAX_NUM_OF_SUPPORT_SENSOR];
atomic_t imgsensor_open_cnt;
enum IMGSENSOR_RETURN (*mclk_set_drive_current)
(void *pinstance,
enum IMGSENSOR_SENSOR_IDX sensor_idx,
enum ISP_DRIVING_CURRENT_ENUM drive_current);
3.2.struct IMGSENSOR_HW *phw = &pimgsensor->hw;
struct IMGSENSOR_HW {
struct IMGSENSOR_HW_DEVICE_COMMON common; 平台设备
struct IMGSENSOR_HW_DEVICE *pdev[IMGSENSOR_HW_ID_MAX_NUM]; 上电方式,对应regulator/gpio等
struct IMGSENSOR_HW_SENSOR_POWER
sensor_pwr[IMGSENSOR_SENSOR_IDX_MAX_NUM];
{
struct IMGSENSOR_HW_POWER_INFO *ppwr_info; 哪路供电及供电电压和时序
enum IMGSENSOR_HW_ID id[IMGSENSOR_HW_PIN_MAX_NUM]; 哪路供电
}; 上电方式及上电时序
const char *enable_sensor_by_index[IMGSENSOR_SENSOR_IDX_MAX_NUM];
};
};
3.3.
3.4.alloc_chrdev_region(&pimgsensor->dev_no, 0, 1, IMGSENSOR_DEV_NAME) 动态分配设备号,IMGSENSOR_DEV_NAME:kd_camera_hw
3.5.pimgsensor->pcdev = cdev_alloc(); 动态申请并分配一个新的字符设备
3.6.cdev_init(pimgsensor->pcdev, &gimgsensor_file_operations); 绑定字符设备及其操作函数
static const struct file_operations gimgsensor_file_operations = {
.owner = THIS_MODULE,
.open = imgsensor_open,
.release = imgsensor_release,
.unlocked_ioctl = imgsensor_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = imgsensor_compat_ioctl
#endif
};
3.7.cdev_add(pimgsensor->pcdev, pimgsensor->dev_no, 1) 绑定了字符设备及其设备号并注册进系统中
3.8.pimgsensor->pclass = class_create(THIS_MODULE, "sensordrv"); 态创建设备的逻辑类,在/sys/class/目录下创建一个名为"sensordrv"的文件夹
3.9.pdevice = device_create(pimgsensor->pclass, NULL, pimgsensor->dev_no, NULL, IMGSENSOR_DEV_NAME); 创建设备文件kd_camera_hw,在/dev目录下,通过这个设备文件可以进行读写操作
3.10.pdevice->of_node = of_find_compatible_node(NULL, NULL, "mediatek,imgsensor"); 从根节点在设备树中查找"mediatek,imgsensor",返回要查找的节点
3.11.phw->common.pplatform_device = pplatform_device; pplatform_device为probe传入参数,平台设备
3.12.imgsensor_hw_init(phw); 上电模块初始化
3.12.1.
struct IMGSENSOR_HW_SENSOR_POWER *psensor_pwr;
struct IMGSENSOR_HW_CFG *pcust_pwr_cfg;
struct IMGSENSOR_HW_CFG imgsensor_custom_config[] = {
{
IMGSENSOR_SENSOR_IDX_MAIN,
IMGSENSOR_I2C_DEV_0,
{
{IMGSENSOR_HW_PIN_MCLK, IMGSENSOR_HW_ID_MCLK}, ppwr_info{IMGSENSOR_HW_PIN, IMGSENSOR_HW_ID}
{IMGSENSOR_HW_PIN_AVDD, IMGSENSOR_HW_ID_GPIO},
{IMGSENSOR_HW_PIN_DOVDD, IMGSENSOR_HW_ID_REGULATOR},
{IMGSENSOR_HW_PIN_DVDD, IMGSENSOR_HW_ID_GPIO},
{IMGSENSOR_HW_PIN_PDN, IMGSENSOR_HW_ID_GPIO},
{IMGSENSOR_HW_PIN_RST, IMGSENSOR_HW_ID_GPIO},
{IMGSENSOR_HW_PIN_NONE, IMGSENSOR_HW_ID_NONE},
}
{
...
}
struct IMGSENSOR_HW_CUSTOM_POWER_INFO *ppwr_info;
{IMGSENSOR_HW_PIN_MCLK, IMGSENSOR_HW_ID_MCLK}
struct device_node *of_node = of_find_compatible_node(NULL, NULL, "mediatek,imgsensor");
3.12.2.遍历设置的IMGSENSOR_SENSOR_IDX_MAX_NUM,根据cameraidx依次进行上电结构体的匹配赋值
pcust_pwr_cfg = imgsensor_custom_config; 拿到imgsensor_cfg_table.c中填入的pcust_pwr_cfg = imgsensor_custom_config的地址/*这里是bringup需要客制化的地方*/,就是平台提供的各路供电接口及其上电方式,比如上面的IMGSENSOR_SENSOR_IDX_MAIN使用i2c为IMGSENSOR_I2C_DEV_0,avdd使用gpio进行供电,iovdd使用regulator进行供电。通过pcust_pwr_cfg地址+1的方式拿取第二第三颗sensor。
snprintf(str_prop_name, izeof(str_prop_name), "cam%d_pin_%s", i, imgsensor_hw_pin_names[ppwr_info->pin]); 根据ppwr_info->pin比如上面定义的IMGSENSOR_HW_PIN_AVDD得到str_prop_name="cam0_pin_vcama"。
of_property_read_string(of_node, str_prop_name, &pin_hw_id_name) 在设备树中寻找cam0_pin_vcama并返回对应的pin_hw_id_name:gpio
再循环IMGSENSOR_HW_ID_MAX_NUM(mclk/regulator/gpio),如果上面返回的pin_hw_id_name==这个检索(mclk/regulator/gpio),ppwr_info->id = 这个检索,ppwr_info->pin=3(avdd),ppwr_info->id=2(gpio)
具体没懂3.12.2.这个操作的意义是什么,相比v1多了个对dts的检索操作,本来imgsensor_custom_config这个表格就已经将avdd和gpio匹配了
3.12.3.遍历IMGSENSOR_HW_ID_MAX_NUM(mclk/regulator/gpio)调用各自init
3.12.4.遍历设置的IMGSENSOR_SENSOR_IDX_MAX_NUM
***psensor_pwr = &phw->sensor_pwr[i]; 将IMGSENSOR_HW_SENSOR_POWER赋给psensor_pwr,这个结构体实际就是将imgsensor_custom_config的每路的供电方式和这路供电的电压/延时绑定在了一起
struct IMGSENSOR_HW_SENSOR_POWER {
struct IMGSENSOR_HW_POWER_INFO *ppwr_info; {哪路供电,电压,延时}
enum IMGSENSOR_HW_ID id[IMGSENSOR_HW_PIN_MAX_NUM]; 供电方式
};
pcust_pwr_cfg = imgsensor_custom_config; 根据cameraidx依次进行上电结构体的匹配赋值
ppwr_info = pcust_pwr_cfg->pwr_info; 拿到imgsensor_custom_config中IMGSENSOR_HW_PIN和IMGSENSOR_HW_ID的匹配关系(avdd通过gpio上电)
while (ppwr_info->pin != IMGSENSOR_HW_PIN_NONE) {
for (j = 0; j < IMGSENSOR_HW_ID_MAX_NUM && ppwr_info->id != phw->pdev[j]->id; j++) {} 遍历IMGSENSOR_HW_ID(mclk/regulator/gpio)
psensor_pwr->id[ppwr_info->pin] = j; ***这里保存了每一路的供电方式和这路供电的电压/延时给到&phw->sensor_pwr[i]供后续上电使用
ppwr_info++;
}
3.12.5.在dts中读取cam0_enable_sensor返回给phw->enable_sensor_by_index 就是sensor name
3.13.imgsensor_i2c_create(); 各sensor i2c注册
i2c_add_driver(&gi2c_driver[i]);
static struct i2c_driver gi2c_driver[IMGSENSOR_I2C_DEV_MAX_NUM] = {
{
.probe = imgsensor_i2c_probe_0,
.remove = imgsensor_i2c_remove,
.driver = {
.name = IMGSENSOR_I2C_DRV_NAME_0,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = gof_device_id_0, 匹配dws中IMGSENSOR_I2C_OF_DRV_NAME_0("mediatek,camera_main"),dws中配置了camera_main的i2c地址
#endif
},
.id_table = gi2c_dev_id,
},
...
}
3.14.imgsensor_proc_init(); 创建一个proc虚拟文件,应用层通过读写该文件,即可实现与内核的交互
3.15.imgsensor_init_sensor_list();
struct IMGSENSOR *pimgsensor = &gimgsensor;
struct IMGSENSOR_SENSOR_LIST *psensor_list = gimgsensor_sensor_list; 获取sensorlist,客制化地方,加入需要的sensor
of_property_read_string(of_node, "cust-sensor", &penable_sensor); 在of_node("mediatek,imgsensor")节点中寻找cust-sensor返回给penable_sensor,在gimgsensor_sensor_list中找到与penable_sensor一致的sensor,由于gimgsensor是全局的引用,所以最终一致的sensor会存在gimgsensor.psensor_list中
至此,camera驱动挂载与初始化完成
上电
camera的上电是在开机时cameraprovider起来时通过searchSensor调用的,kernel的调用从imgsensor_ioctl()开始
1.adopt_CAMERA_HW_FeatureControl()
struct IMGSENSOR_SENSOR *psensor; 包含SENSOR_INST和SENSOR_FUNCTION
psensor = imgsensor_sensor_get_inst(pFeatureCtrl->InvokeCamera); 根据上层传下来的cameraid返回底层的psensor
psensor->inst.sensor_idx = pFeatureCtrl->InvokeCamera; 拿到底层的cameraid
v1通过这里遍历去给各sensoridx上电
1.1.imgsensor_set_driver()
struct IMGSENSOR *pimgsensor = &gimgsensor;
struct IMGSENSOR_SENSOR_INST *psensor_inst = &psensor->inst;
v1_1通过这里遍历MAX_NUM_OF_SUPPORT_SENSOR给各sensor上电
pimgsensor->psensor_list[i]->init(&psensor->pfunc); 拿到sensor的函数指针
psensor_inst->psensor_list = pimgsensor->psensor_list[i]; 将gimgsensor之前在imgsensor_init_sensor_list()拿到的sensorlist赋给psensor_inst->psensor_list
2.imgsensor_check_is_alive(psensor)
struct IMGSENSOR *pimgsensor = &gimgsensor;
struct IMGSENSOR_SENSOR_INST *psensor_inst = &psensor->inst;
2.1.imgsensor_hw_power(&pimgsensor->hw, psensor, IMGSENSOR_HW_POWER_STATUS_ON); 传入IMGSENSOR_HW_POWER_STATUS_ON为上电
2.1.1.imgsensor_hw_power_sequence(phw, sensor_idx, pwr_status, sensor_power_sequence, curr_sensor_name); sensor_power_sequence是客制化的上电时序
struct IMGSENSOR_HW_SENSOR_POWER *psensor_pwr = &phw->sensor_pwr[sensor_idx]; 获取之前拿到的上电配置(哪路电使用哪种上电方式,上电电压是多少,上电延时)
struct IMGSENSOR_HW_POWER_SEQ *ppwr_seq = ppower_sequence;
strcmp(ppwr_seq->name, pcurr_idx) 遍历上电时序表ppower_sequence,根据传入的sensor的name,匹配一致拿取这个sensor的时序表
ppwr_info = ppwr_seq->pwr_info;
遍历需要上电模组的上电时序表ppower_sequence里的ppwr_info->pin
pdev = phw->pdev[psensor_pwr->id[ppwr_info->pin]]; 拿到之前init时保存的这个pin所对应的上电方式(id:mclk/regulator/mclk)的dev
pdev->set(pdev->pinstance, sensor_idx, ppwr_info->pin/*pin脚*/, ppwr_info->pin_state_on/*上电电压*/); 到各自上电模块dev中进行set上电
mdelay(ppwr_info->pin_on_delay); delay延时
至此完成上电
2.2.imgsensor_sensor_feature_control(psensor, SENSOR_FEATURE_CHECK_SENSOR_ID, (MUINT8 *) &sensorID, &retLen); 调用sensor驱动get_imgsensor_id接口获取sensor id
2.3.mgsensor_hw_power(&pimgsensor->hw, psensor, IMGSENSOR_HW_POWER_STATUS_OFF); 下电
1.2.psensor_list->id = psensor->inst.psensor_list->id;
memcpy(psensor_list->name, psensor->inst.psensor_list->name, 32);
Regulator
regulator_init()
1.struct REGULATOR *preg //创建 REGULATOR,其中包含其regulator设备以及原子锁count
struct regulator {
struct device *dev;
struct list_head list;
unsigned int always_on:1;
unsigned int bypass:1;
int uA_load;
int min_uV;
int max_uV;
const char *supply_name;
struct device_attribute dev_attr;
struct regulator_dev *rdev;
struct dentry *debugfs;
};
2.根据底层IMGSENSOR_SENSOR_IDX以及自身定义的regulator_control(vcama/vcamd/vcamio)获取所有的cam和三路电的排列组合,组合成str_regulator_name(cam0_vcama),regulator_get_optional通过dev以及str_regulator_name获取regulator资源regulator[idx][type]供后续上下电使用,cam0_vcama对应dtsi中的kd_camera_hw1,kd_camera_hw1将其各路电和引脚绑定,这里cam0_vcamio是regulator供电,cam0_vcamio-supply = <&mt_pmic_vcamio_ldo_reg>;其他的都是gpio供电
regulator_set()
1.imgsensor_hw.c的imgsensor_hw_power_sequence()将sensor_idx,pin哪路电和电压传入regulator_set()
2.regulator中只定义了vcama(0),vcamd(1),vcamio(2)三路电,但是在cfg_table中除了regulator上电还会有其他上电方式的enum,所以传入的上电方式enum和regulator的上电方式enum存在偏移量IMGSENSOR_HW_PIN_AVDD
reg_type_offset = REGULATOR_TYPE_VCAMA;//REGULATOR_TYPE_VCAMA为0,不知道具体作用?
pregulator = preg->pregulator[(unsigned int)sensor_idx][reg_type_offset + pin - IMGSENSOR_HW_PIN_AVDD];
regulator的上电enum:
enum REGULATOR_TYPE {
REGULATOR_TYPE_VCAMA,
REGULATOR_TYPE_VCAMD,
REGULATOR_TYPE_VCAMIO,
REGULATOR_TYPE_MAX_NUM
}
cfg_table的上电enum:
前三个对于regulator是无用的,所以偏移就为IMGSENSOR_HW_PIN_AVDD 3
enum IMGSENSOR_HW_PIN {
IMGSENSOR_HW_PIN_NONE = 0,
IMGSENSOR_HW_PIN_PDN,
IMGSENSOR_HW_PIN_RST,
IMGSENSOR_HW_PIN_AVDD,
IMGSENSOR_HW_PIN_DVDD,
IMGSENSOR_HW_PIN_DOVDD,
}
3.绑定了regulator后regulator_set_voltage(regulator, min voltage, max voltage),regulator_enable(pregulator)完成上电
gpio
gpio_init()
1.同样创建gpio设备
2.devm_pinctrl_get通过dev获取pinctrl资源节点
3.根据IMGSENSOR_SENSOR_IDX和gpio_pinctrl_list_cam(ldo_vcama_1)获取所有排列组合组合成str_pinctrl_name(cam0_ldo_vcama_1)
4.pinctrl_lookup_state根据pinctrl资源和str_pinctrl_name获取gpio资源gpio->ppinctrl_state_cam[j][i]供后续上下电使用,同样也是在kd_camera_hw1中
gpio_set()
1.获取传下来的电压值gpio_state,非0为GPIO_STATE_H, 0为GPIO_STATE_L
2.ppinctrl_state = pgpio->ppinctrl_state_cam[sensor_idx][((pin - IMGSENSOR_HW_PIN_PDN) << 1) + gpio_state]; //gpio的enum相较于cfg_table的多了low和high,所以会有这个移位操作?
上下电取决于gpio_state,上电获取ppinctrl_state_cam[0]的GPIO_CTRL_STATE_RST_L,下电为GPIO_CTRL_STATE_RST_H
3.pinctrl_select_state(pgpio->ppinctrl, ppinctrl_state); pinctrl_select_state通过pinctrl资源和ppinctrl_state进行状态的选择
最后
以上就是超级紫菜为你收集整理的MTK CAMERA ISP6S Kernel的全部内容,希望文章能够帮你解决MTK CAMERA ISP6S Kernel所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复