camera的硬件关系:
camera相关DTS配置
以下为i2c与csi间的对应关系:
&i2c0 {
status = "okay";clock-frequency = <400000>;sensor_main2: sensor-main2@20 {
//imx258 back extcompatible = "sprd,sensor-main2";reg = <0x20>;clock-names = "clk_src","sensor_eb","clk_96m","clk_76m8","clk_48m","clk_26m";clocks = <&mm_clk CLK_SENSOR0>, <&mm_gate CLK_SENSOR0_EB>,<&pll CLK_TWPLL_96M>,<&pll CLK_TWPLL_76M8>,<&pll CLK_TWPLL_48M>,<&ext_26m>;vddio-supply = <&vddcamio>;vddcama-supply = <&vddcama>;vddcamd-supply = <&vddcamd>;vddcammot-supply = <&vddcammot>;sprd,phyid = <1>;csi = <&csi0>;reset-gpios = <&ap_gpio 44 0>;power-down-gpios = <&ap_gpio 46 0>;};
};&i2c1 {
status = "okay";clock-frequency = <400000>;sensor_sub: sensor-sub@20 {
//sp2509z frontcompatible = "sprd,sensor-sub";reg = <0x20>;clock-names = "clk_src","sensor_eb","clk_96m","clk_76m8","clk_48m","clk_26m";clocks = <&mm_clk CLK_SENSOR1>, <&mm_gate CLK_SENSOR1_EB>,<&pll CLK_TWPLL_96M>,<&pll CLK_TWPLL_76M8>,<&pll CLK_TWPLL_48M>,<&ext_26m>;vddio-supply = <&vddcamio>;vddcama-supply = <&vdd28>;vddcamd-supply = <&vddcamd>;sprd,phyid = <3>;csi = <&csi1>;reset-gpios = <&ap_gpio 45 0>;power-down-gpios = <&ap_gpio 47 0>;};sensor_main: sensor-main@6C {
//ov5648 backcompatible = "sprd,sensor-main";reg = <0x6C>;clock-names = "clk_src","sensor_eb","clk_96m","clk_76m8","clk_48m","clk_26m";clocks = <&mm_clk CLK_SENSOR1>, <&mm_gate CLK_SENSOR1_EB>,<&pll CLK_TWPLL_96M>,<&pll CLK_TWPLL_76M8>,<&pll CLK_TWPLL_48M>,<&ext_26m>;vddio-supply = <&vddcamio>;vddcama-supply = <&vdd28>;vddcamd-supply = <&vddcamd>;vddcammot-supply = <&vdd28>;sprd,phyid = <2>;csi = <&csi1>;reset-gpios = <&ap_gpio 41 0>;power-down-gpios = <&ap_gpio 40 0>;//dvdd-gpios = <&ap_gpio 62 0>;};
};
sensor driver初始化流程
static int sprd_sensor_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
struct device *dev = &client->dev;int i, ret = 0;struct sprd_sensor_dev_info_tag *pdata = NULL;pr_info("dcam sensor probe start:device name:%s\n", id->name);if (!dev->of_node) {
pr_err("no device node %s", __func__);return -ENODEV;}pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);if (!pdata)return -ENOMEM;client->dev.platform_data = (void *)pdata;pdata->i2c_info = client;for (i = 0; i < SENSOR_REGULATOR_ID_MAX; i++)pdata->regulator_supply[i] = NULL;mutex_init(&pdata->sync_lock);mutex_init(&pdata->set_voltage_lock);atomic_set(&pdata->users, 0);pdata->sensor_id = SPRD_SENSOR_ID_MAX;if (of_device_is_compatible(dev->of_node, "sprd,sensor-main")) {
pdata->sensor_id = SPRD_SENSOR_MAIN_ID_E;ret = sprd_sensor_config(dev, pdata);s_sensor_dev_data[SPRD_SENSOR_MAIN_ID_E] = pdata;} else if (of_device_is_compatible(dev->of_node, "sprd,sensor-sub")) {
pdata->sensor_id = SPRD_SENSOR_SUB_ID_E;ret = sprd_sensor_config(dev, pdata);s_sensor_dev_data[SPRD_SENSOR_SUB_ID_E] = pdata;} else if (of_device_is_compatible(dev->of_node, "sprd,sensor-main2")) {
pdata->sensor_id = SPRD_SENSOR_MAIN2_ID_E;ret = sprd_sensor_config(dev, pdata);s_sensor_dev_data[SPRD_SENSOR_MAIN2_ID_E] = pdata;} else if (of_device_is_compatible(dev->of_node, "sprd,sensor-sub2")) {
pdata->sensor_id = SPRD_SENSOR_SUB2_ID_E;ret = sprd_sensor_config(dev, pdata);s_sensor_dev_data[SPRD_SENSOR_SUB2_ID_E] = pdata;} else if (of_device_is_compatible(dev->of_node, "sprd,sensor-main3")) {
pdata->sensor_id = SPRD_SENSOR_MAIN3_ID_E;ret = sprd_sensor_config(dev, pdata);s_sensor_dev_data[SPRD_SENSOR_MAIN3_ID_E] = pdata;} else if (of_device_is_compatible(dev->of_node, "sprd,sensor-sub3")) {
pdata->sensor_id = SPRD_SENSOR_SUB3_ID_E;ret = sprd_sensor_config(dev, pdata);s_sensor_dev_data[SPRD_SENSOR_SUB3_ID_E] = pdata;}return ret;
}
主要关注函数sprd_sensor_config的实现
static int sprd_sensor_config(struct device *dev,struct sprd_sensor_dev_info_tag *sensor_info)
{
struct device_node *csi_ep_node = NULL;unsigned int phy_id = 0;int ret = 0;ret = sprd_sensor_parse_dt(dev, sensor_info);if (ret) {
pr_err("%s :parse dt error\n", __func__);goto exit;}sensor_info->dev_node = dev->of_node;phy_id = csi_api_mipi_phy_cfg_init(sensor_info->dev_node,sensor_info->sensor_id);if (phy_id < 0U){
pr_err("sprd,phyid is invalid\n");return -1;}csi_ep_node = get_csi_port_node(sensor_info->dev_node);if (csi_ep_node) {
pr_info("%lu\n", (unsigned long)csi_ep_node);csi_api_dt_node_init(dev, csi_ep_node, sensor_info->sensor_id,phy_id);} elsepr_err("%s; csi sensor connection error\n", __func__);if (parse_dcam_id(csi_ep_node, sensor_info)) {
pr_err("%s :dcam id parsing error\n", __func__);return -EINVAL;}exit:return ret;
}
接着主要关注函数csi_api_dt_node_init的实现
int csi_api_dt_node_init(struct device *dev, struct device_node *dn,int sensor_id, unsigned int phy_id)
{
struct csi_dt_node_info *csi_info = NULL;struct resource res;void __iomem *reg_base = NULL;struct regmap *regmap_syscon = NULL;csi_info = devm_kzalloc(dev, sizeof(*csi_info), GFP_KERNEL);if (!csi_info)return -EINVAL;/* read address */if (of_address_to_resource(dn, 0, &res)) {
pr_err("csi2_init:fail to get address info\n");return -EINVAL;}reg_base = devm_ioremap_nocache(dev, res.start, resource_size(&res));if (IS_ERR_OR_NULL(reg_base)) {
pr_err("csi_dt_init:fail to get csi regbase\n");return PTR_ERR(reg_base);}csi_info->reg_base = (unsigned long)reg_base;csi_info->phy.phy_id = phy_id;pr_info("csi node name %s\n", dn->name);if (of_property_read_u32_index(dn, "sprd,csi-id", 0,&csi_info->controller_id)) {
pr_err("csi2_init:fail to get csi-id\n");return -EINVAL;}pr_info("csi %d ,phy addr:0x%"PRIx64" ,size:0x%"PRIx64" , reg %lx\n",csi_info->controller_id, res.start,resource_size(&res),csi_info->reg_base);/* read clocks */csi_info->clk_ckg_eb = of_clk_get_by_name(dn,"clk_gate_eb");csi_info->mipi_csi_gate_eb = of_clk_get_by_name(dn,"clk_mipi_csi_gate_eb");csi_info->csi_eb_clk = of_clk_get_by_name(dn, "clk_csi_eb");/* csi src flag from sensor or csi */csi_info->csi_src_eb = of_clk_get_by_name(dn, "mipi_csi_src_eb");regmap_syscon = syscon_regmap_lookup_by_phandle(dn,"sprd,cam-ahb-syscon");if (IS_ERR_OR_NULL(regmap_syscon)) {
pr_err("csi_dt_init:fail to get cam-ahb-syscon\n");return PTR_ERR(regmap_syscon);}csi_info->phy.cam_ahb_syscon = regmap_syscon;regmap_syscon = syscon_regmap_lookup_by_phandle(dn,"sprd,aon-apb-syscon");if (IS_ERR_OR_NULL(regmap_syscon)) {
pr_err("csi_dt_init:fail to get aon-apb-syscon\n");return PTR_ERR(regmap_syscon);}csi_info->phy.aon_apb_syscon = regmap_syscon;regmap_syscon = syscon_regmap_lookup_by_phandle(dn,"sprd,anlg_phy_g1_controller");if (IS_ERR_OR_NULL(regmap_syscon)) {
pr_err("csi_dt_init:fail to get anlg_phy_g1_controller\n");return PTR_ERR(regmap_syscon);}csi_info->phy.anlg_phy_g1_syscon = regmap_syscon;csi_reg_base_save(csi_info, sensor_id);csi_phy_power_down(csi_info, sensor_id, 1);s_csi_dt_info_p[sensor_id] = csi_info;pr_info("csi dt info:sensor_id :%d, csi_info:0x%p\n",sensor_id, csi_info);return 0;
}
接着关键的地方来了,就会关联到sensor对应的dcam
static uint32_t parse_dcam_id(struct device_node *dn,struct sprd_sensor_dev_info_tag *sensor_info)
{
int dcam_id = -1;sensor_info->attch_dcam_id = -1;if (of_property_read_u32(dn, "sprd,dcam-id", &dcam_id)) {
pr_err("Get dcam id error\n");return -1;}pr_info("dcam id is %d\n", dcam_id);sensor_info->attch_dcam_id = dcam_id;return 0;
}
user space camera初始化流程
//首先open device设备p_grab->fd = open("/dev/sprd_image", O_RDWR, 0);...
//通过sensor_id获取相关的硬件资源ret = ioctl(p_grab->fd, SPRD_IMG_IO_GET_DCAM_RES, &res);
通过上面代码就可以获得指定sensor_id对应的资源,因为camera driver在kernel层是多实例进行实现的,接着就可以通过ioctl进行相关操作。
在camcore_open中通过grp->module_used来标志DCAMx是否被使用,如果没有被使用则进行相关硬件初始化。
static int camcore_open(struct inode *node, struct file *file)
{
int ret = 0;unsigned long flag;struct camera_module *module = NULL;struct camera_group *grp = NULL;struct miscdevice *md = file->private_data;uint32_t i, idx, count = 0;grp = md->this_device->platform_data;count = grp->dcam_count;if (count == 0 || count > CAM_COUNT) {
pr_err("fail to get valid dts configured dcam count\n");return -ENODEV;}if (atomic_inc_return(&grp->camera_opened) > count) {
pr_err("fail to open camera, all %d cameras opened already.", count);atomic_dec(&grp->camera_opened);return -EMFILE;}pr_info("sprd_img: the camera opened count %d\n",atomic_read(&grp->camera_opened));pr_info("camca: camsec_mode = %d\n", grp->camsec_cfg.camsec_mode);spin_lock_irqsave(&grp->module_lock, flag);for (i = 0, idx = count; i < count; i++) {
if ((grp->module_used & (1 << i)) == 0) {
if (grp->module[i] != NULL) {
pr_err("fail to get null module, un-release camera module: %p, idx %d\n",grp->module[i], i);spin_unlock_irqrestore(&grp->module_lock, flag);ret = -EMFILE;goto exit;}idx = i;grp->module_used |= (1 << i);break;}}spin_unlock_irqrestore(&grp->module_lock, flag);if (idx == count) {
pr_err("fail to get available camera module.\n");ret = -EMFILE;goto exit;}pr_debug("kzalloc. size of module %x, group %x\n",(int)sizeof(struct camera_module),(int) sizeof(struct camera_group));module = vzalloc(sizeof(struct camera_module));if (!module) {
pr_err("fail to alloc camera module %d\n", idx);ret = -ENOMEM;goto alloc_fail;}module->idx = idx;ret = camcore_module_init(module);if (ret) {
pr_err("fail to init camera module %d\n", idx);ret = -ENOMEM;goto init_fail;}if (atomic_read(&grp->camera_opened) == 1) {
/* should check all needed interface here. */if (grp->hw_info && grp->hw_info->soc_dcam->pdev)ret = cam_buf_iommudev_reg(&grp->hw_info->soc_dcam->pdev->dev,CAM_IOMMUDEV_DCAM);if (grp->hw_info && grp->hw_info->soc_isp->pdev)ret = cam_buf_iommudev_reg(&grp->hw_info->soc_isp->pdev->dev,CAM_IOMMUDEV_ISP);g_empty_frm_q = &grp->empty_frm_q;cam_queue_init(g_empty_frm_q, CAM_EMP_Q_LEN_MAX,cam_queue_empty_frame_free);g_empty_state_q = &grp->empty_state_q;cam_queue_init(g_empty_state_q, CAM_EMP_Q_LEN_MAX,cam_queue_empty_state_free);pr_info("init frm_q %px state_q %px\n", g_empty_frm_q, g_empty_state_q);}module->idx = idx;module->grp = grp;grp->module[idx] = module;file->private_data = (void *)module;pr_info("sprd_img: open end! %d, %px, %px, grp %px\n",idx, module, grp->module[idx], grp);return 0;init_fail:vfree(module);alloc_fail:spin_lock_irqsave(&grp->module_lock, flag);grp->module_used &= ~(1 << idx);grp->module[idx] = NULL;spin_unlock_irqrestore(&grp->module_lock, flag);exit:atomic_dec(&grp->camera_opened);pr_err("fail to open camera %d\n", ret);return ret;
}