Loading drivers/media/platform/msm/camera/cam_sensor_module/Makefile +1 −0 Original line number Diff line number Diff line obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_utils/ obj-$(CONFIG_SPECTRA_CAMERA) += cam_cci/ obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_io/ obj-$(CONFIG_SPECTRA_CAMERA) += cam_csiphy/ drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/Makefile 0 → 100644 +8 −0 Original line number Diff line number Diff line ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr obj-$(CONFIG_SPECTRA_CAMERA) += cam_csiphy_soc.o cam_csiphy_dev.o cam_csiphy_core.o drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c 0 → 100644 +500 −0 Original line number Diff line number Diff line /* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * 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. */ #include <linux/module.h> #include "cam_csiphy_core.h" #include "cam_csiphy_dev.h" #include "cam_csiphy_soc.h" #include <cam_mem_mgr.h> void cam_csiphy_query_cap(struct csiphy_device *csiphy_dev, struct cam_csiphy_query_cap *csiphy_cap) { csiphy_cap->slot_info = csiphy_dev->v4l2_dev_str.pdev->id; csiphy_cap->version = csiphy_dev->hw_version; csiphy_cap->clk_lane = csiphy_dev->clk_lane; } void cam_csiphy_reset(struct csiphy_device *csiphy_dev) { int32_t i; uint32_t size = csiphy_dev->ctrl_reg->csiphy_reg.csiphy_reset_array_size; for (i = 0; i < size; i++) { cam_io_w( csiphy_dev->ctrl_reg-> csiphy_reset_reg[i].reg_data, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_reset_reg[i].reg_addr); usleep_range(csiphy_dev->ctrl_reg-> csiphy_reset_reg[i].delay * 100, csiphy_dev->ctrl_reg-> csiphy_reset_reg[i].delay * 100 + 1000); } } int32_t cam_cmd_buf_parser(struct csiphy_device *csiphy_dev, struct cam_config_dev_cmd *cfg_dev) { int32_t rc = 0; uint64_t generic_ptr; struct cam_packet *csl_packet = NULL; struct cam_cmd_buf_desc *cmd_desc = NULL; uint32_t *cmd_buf = NULL; struct cam_csiphy_info *cam_cmd_csiphy_info = NULL; size_t len; if (!cfg_dev || !csiphy_dev) { pr_err("%s:%d Invalid Args\n", __func__, __LINE__); return -EINVAL; } csiphy_dev->csiphy_info = kzalloc(sizeof(struct cam_csiphy_info), GFP_KERNEL); if (!csiphy_dev->csiphy_info) return -ENOMEM; rc = cam_mem_get_cpu_buf((int32_t) cfg_dev->packet_handle, (uint64_t *)&generic_ptr, &len); if (rc < 0) { pr_err("%s:%d :ERROR: Failed to get packet Mem address: %d\n", __func__, __LINE__, rc); kfree(csiphy_dev->csiphy_info); csiphy_dev->csiphy_info = NULL; return rc; } if (cfg_dev->offset > len) { pr_err("%s: %d offset is out of bounds: offset: %lld len: %zu\n", __func__, __LINE__, cfg_dev->offset, len); kfree(csiphy_dev->csiphy_info); csiphy_dev->csiphy_info = NULL; return -EINVAL; } csl_packet = (struct cam_packet *)(generic_ptr + cfg_dev->offset); cmd_desc = (struct cam_cmd_buf_desc *) ((uint32_t *)&csl_packet->payload + csl_packet->cmd_buf_offset / 4); rc = cam_mem_get_cpu_buf(cmd_desc->mem_handle, (uint64_t *)&generic_ptr, &len); if (rc < 0) { pr_err("%s:%d :ERROR: Failed to get cmd buf Mem address : %d\n", __func__, __LINE__, rc); kfree(csiphy_dev->csiphy_info); csiphy_dev->csiphy_info = NULL; return rc; } cmd_buf = (uint32_t *)generic_ptr; cmd_buf += cmd_desc->offset / 4; cam_cmd_csiphy_info = (struct cam_csiphy_info *)cmd_buf; csiphy_dev->csiphy_info->lane_cnt = cam_cmd_csiphy_info->lane_cnt; csiphy_dev->csiphy_info->lane_mask = cam_cmd_csiphy_info->lane_mask; csiphy_dev->csiphy_info->csiphy_3phase = cam_cmd_csiphy_info->csiphy_3phase; csiphy_dev->csiphy_info->combo_mode = cam_cmd_csiphy_info->combo_mode; csiphy_dev->csiphy_info->settle_time = cam_cmd_csiphy_info->settle_time; csiphy_dev->csiphy_info->data_rate = cam_cmd_csiphy_info->data_rate; return rc; } void cam_csiphy_cphy_irq_config(struct csiphy_device *csiphy_dev) { int32_t i; for (i = 0; i < csiphy_dev->num_irq_registers; i++) cam_io_w(csiphy_dev->ctrl_reg-> csiphy_irq_reg[i].reg_data, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_irq_reg[i].reg_addr); } void cam_csiphy_cphy_irq_disable(struct csiphy_device *csiphy_dev) { int32_t i; for (i = 0; i < csiphy_dev->num_irq_registers; i++) cam_io_w(0x0, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_irq_reg[i].reg_addr); } irqreturn_t cam_csiphy_irq(int irq_num, void *data) { uint32_t irq; uint8_t i; struct csiphy_device *csiphy_dev = (struct csiphy_device *)data; if (!csiphy_dev) { pr_err("%s:%d Invalid Args\n", __func__, __LINE__); return -EINVAL; } for (i = 0; i < csiphy_dev->num_irq_registers; i++) { irq = cam_io_r( csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_reg. mipi_csiphy_interrupt_status0_addr + 0x4*i); cam_io_w(irq, csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_reg. mipi_csiphy_interrupt_clear0_addr + 0x4*i); pr_err_ratelimited( "%s CSIPHY%d_IRQ_STATUS_ADDR%d = 0x%x\n", __func__, csiphy_dev->v4l2_dev_str.pdev->id, i, irq); cam_io_w(0x0, csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_reg. mipi_csiphy_interrupt_clear0_addr + 0x4*i); } cam_io_w(0x1, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_reg.mipi_csiphy_glbl_irq_cmd_addr); cam_io_w(0x0, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_reg.mipi_csiphy_glbl_irq_cmd_addr); return IRQ_HANDLED; } int32_t cam_csiphy_config_dev(struct csiphy_device *csiphy_dev) { int32_t rc = 0; uint32_t lane_enable = 0, mask = 1, size = 0; uint16_t lane_mask = 0, i = 0, cfg_size = 0; uint8_t settle_cnt, lane_cnt, lane_pos = 0; void __iomem *csiphybase; struct csiphy_reg_t (*reg_array)[MAX_SETTINGS_PER_LANE]; if (csiphy_dev->csiphy_info == NULL) { pr_err("%s:%d csiphy_info is NULL, No/Fail CONFIG_DEV ?\n", __func__, __LINE__); return -EINVAL; } lane_cnt = csiphy_dev->csiphy_info->lane_cnt; lane_mask = csiphy_dev->csiphy_info->lane_mask & 0x1f; settle_cnt = (csiphy_dev->csiphy_info->settle_time / 200000000); csiphybase = csiphy_dev->base; if (!csiphybase) { pr_err("%s: csiphybase NULL\n", __func__); return -EINVAL; } for (i = 0; i < MAX_DPHY_DATA_LN; i++) { if (mask == 0x2) { if (lane_mask & mask) lane_enable |= 0x80; i--; } else if (lane_mask & mask) { lane_enable |= 0x1 << (i<<1); } mask <<= 1; } if (!csiphy_dev->csiphy_info->csiphy_3phase) { if (csiphy_dev->csiphy_info->combo_mode == 1) reg_array = csiphy_dev->ctrl_reg->csiphy_2ph_combo_mode_reg; else reg_array = csiphy_dev->ctrl_reg->csiphy_2ph_reg; csiphy_dev->num_irq_registers = 11; cfg_size = csiphy_dev->ctrl_reg->csiphy_reg. csiphy_2ph_config_array_size; } else { if (csiphy_dev->csiphy_info->combo_mode == 1) reg_array = csiphy_dev->ctrl_reg->csiphy_2ph_3ph_mode_reg; else reg_array = csiphy_dev->ctrl_reg->csiphy_3ph_reg; csiphy_dev->num_irq_registers = 20; cfg_size = csiphy_dev->ctrl_reg->csiphy_reg. csiphy_3ph_config_array_size; } size = csiphy_dev->ctrl_reg->csiphy_reg.csiphy_common_array_size; for (i = 0; i < size; i++) { switch (csiphy_dev->ctrl_reg-> csiphy_common_reg[i].csiphy_param_type) { case CSIPHY_LANE_ENABLE: cam_io_w(lane_enable, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_common_reg[i].reg_addr); break; case CSIPHY_DEFAULT_PARAMS: cam_io_w(csiphy_dev->ctrl_reg-> csiphy_common_reg[i].reg_data, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_common_reg[i].reg_addr); break; default: break; } } while (lane_mask & 0x1f) { if (!(lane_mask & 0x1)) { lane_pos++; lane_mask >>= 1; continue; } for (i = 0; i < cfg_size; i++) { switch (reg_array[lane_pos][i].csiphy_param_type) { case CSIPHY_LANE_ENABLE: cam_io_w(lane_enable, csiphy_dev->base + reg_array[lane_pos][i].reg_addr); break; case CSIPHY_DEFAULT_PARAMS: cam_io_w(reg_array[lane_pos][i].reg_data, csiphy_dev->base + reg_array[lane_pos][i].reg_addr); break; case CSIPHY_SETTLE_CNT_LOWER_BYTE: cam_io_w(settle_cnt & 0xFF, csiphy_dev->base + reg_array[lane_pos][i].reg_addr); break; case CSIPHY_SETTLE_CNT_HIGHER_BYTE: cam_io_w((settle_cnt >> 8) & 0xFF, csiphy_dev->base + reg_array[lane_pos][i].reg_addr); break; default: CDBG("%s: %d Do Nothing\n", __func__, __LINE__); break; } usleep_range(reg_array[lane_pos][i].delay*1000, reg_array[lane_pos][i].delay*1000 + 1000); } lane_mask >>= 1; lane_pos++; } cam_csiphy_cphy_irq_config(csiphy_dev); return rc; } int32_t cam_csiphy_core_cfg(void *phy_dev, void *arg) { struct csiphy_device *csiphy_dev = (struct csiphy_device *)phy_dev; struct cam_control *cmd = (struct cam_control *)arg; int32_t rc = 0; if (!csiphy_dev || !cmd) { pr_err("%s:%d Invalid input args\n", __func__, __LINE__); return -EINVAL; } pr_debug("%s:%d Opcode received: %d\n", __func__, __LINE__, cmd->op_code); mutex_lock(&csiphy_dev->mutex); switch (cmd->op_code) { case CAM_ACQUIRE_DEV: { struct cam_sensor_acquire_dev csiphy_acq_dev; struct cam_csiphy_acquire_dev_info csiphy_acq_params; struct cam_create_dev_hdl bridge_params; rc = copy_from_user(&csiphy_acq_dev, (void __user *)cmd->handle, sizeof(csiphy_acq_dev)); if (rc < 0) { pr_err("%s:%d :ERROR: Failed copying from User\n", __func__, __LINE__); goto release_mutex; } csiphy_acq_params.combo_mode = 0; if (csiphy_dev->acquire_count == 2) { pr_err("%s:%d CSIPHY device do not allow more than 2 acquires\n", __func__, __LINE__); rc = -EINVAL; goto release_mutex; } bridge_params.ops = NULL; bridge_params.session_hdl = csiphy_acq_dev.session_handle; bridge_params.v4l2_sub_dev_flag = 0; bridge_params.media_entity_flag = 0; bridge_params.priv = csiphy_dev; csiphy_acq_dev.device_handle = cam_create_device_hdl(&bridge_params); csiphy_dev->bridge_intf. device_hdl[csiphy_acq_params.combo_mode] = csiphy_acq_dev.device_handle; csiphy_dev->bridge_intf. session_hdl[csiphy_acq_params.combo_mode] = csiphy_acq_dev.session_handle; if (copy_to_user((void __user *)cmd->handle, &csiphy_acq_dev, sizeof(struct cam_sensor_acquire_dev))) { pr_err("%s:%d :ERROR: Failed copying from User\n", __func__, __LINE__); rc = -EINVAL; goto release_mutex; } if (csiphy_acq_params.combo_mode == 1) csiphy_dev->is_acquired_dev_combo_mode = 1; csiphy_dev->acquire_count++; } break; case CAM_QUERY_CAP: { struct cam_csiphy_query_cap csiphy_cap; cam_csiphy_query_cap(csiphy_dev, &csiphy_cap); if (copy_to_user((void __user *)cmd->handle, &csiphy_cap, sizeof(struct cam_csiphy_query_cap))) { pr_err("%s:%d :ERROR: Failed copying from User\n", __func__, __LINE__); rc = -EINVAL; goto release_mutex; } } break; case CAM_STOP_DEV: { rc = cam_csiphy_soc_release(csiphy_dev); if (rc < 0) { pr_err("%s:%d Failed in csiphy release\n", __func__, __LINE__); cam_cpas_stop(csiphy_dev->cpas_handle); goto release_mutex; } rc = cam_cpas_stop(csiphy_dev->cpas_handle); if (rc < 0) { pr_err("%s:%d :Error: de-voting CPAS: %d\n", __func__, __LINE__, rc); goto release_mutex; } } break; case CAM_RELEASE_DEV: { struct cam_release_dev_cmd release; if (!csiphy_dev->acquire_count) { pr_err("%s:%d No valid devices to release\n", __func__, __LINE__); rc = -EINVAL; goto release_mutex; } if (copy_from_user(&release, (void __user *) cmd->handle, sizeof(release))) { rc = -EFAULT; goto release_mutex; } rc = cam_destroy_device_hdl(release.dev_handle); if (rc < 0) pr_err("%s:%d :ERROR: destroying the device hdl\n", __func__, __LINE__); if (release.dev_handle == csiphy_dev->bridge_intf.device_hdl[0]) { csiphy_dev->bridge_intf.device_hdl[0] = -1; csiphy_dev->bridge_intf.link_hdl[0] = -1; csiphy_dev->bridge_intf.session_hdl[0] = -1; } else { csiphy_dev->bridge_intf.device_hdl[1] = -1; csiphy_dev->bridge_intf.link_hdl[1] = -1; csiphy_dev->bridge_intf. session_hdl[1] = -1; } csiphy_dev->acquire_count--; } break; case CAM_CONFIG_DEV: { struct cam_config_dev_cmd config; if (copy_from_user(&config, (void __user *)cmd->handle, sizeof(config))) { rc = -EFAULT; } else { rc = cam_cmd_buf_parser(csiphy_dev, &config); if (rc < 0) { pr_err("%s:%d Fail in cmd buf parser\n", __func__, __LINE__); goto release_mutex; } } break; } case CAM_START_DEV: { struct cam_ahb_vote ahb_vote; struct cam_axi_vote axi_vote; ahb_vote.type = CAM_VOTE_ABSOLUTE; ahb_vote.vote.level = CAM_SVS_VOTE; axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW; axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW; rc = cam_cpas_start(csiphy_dev->cpas_handle, &ahb_vote, &axi_vote); if (rc < 0) { pr_err("%s:%d :Error: voting CPAS: %d\n", __func__, __LINE__, rc); goto release_mutex; } rc = cam_csiphy_enable_hw(csiphy_dev); if (rc != 0) { pr_err("%s: %d cam_csiphy_enable_hw failed\n", __func__, __LINE__); cam_cpas_stop(csiphy_dev->cpas_handle); goto release_mutex; } rc = cam_csiphy_config_dev(csiphy_dev); if (rc < 0) { pr_err("%s: %d cam_csiphy_config_dev failed\n", __func__, __LINE__); cam_cpas_stop(csiphy_dev->cpas_handle); goto release_mutex; } } break; default: pr_err("%s:%d :Error: Invalid Opcode: %d\n", __func__, __LINE__, cmd->op_code); rc = -EINVAL; goto release_mutex; } release_mutex: mutex_unlock(&csiphy_dev->mutex); return rc; } drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h 0 → 100644 +52 −0 Original line number Diff line number Diff line /* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * 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. */ #ifndef _CAM_CSIPHY_CORE_H_ #define _CAM_CSIPHY_CORE_H_ #include <linux/irqreturn.h> #include "cam_csiphy_dev.h" #include <cam_mem_mgr.h> #include <cam_req_mgr_util.h> #include <cam_io_util.h> /** * @csiphy_dev: CSIPhy device structure * * This API programs CSIPhy IRQ registers */ void cam_csiphy_cphy_irq_config(struct csiphy_device *csiphy_dev); /** * @csiphy_dev: CSIPhy device structure * * This API resets CSIPhy hardware */ void cam_csiphy_reset(struct csiphy_device *csiphy_dev); /** * @csiphy_dev: CSIPhy device structure * @arg: Camera control command argument * * This API handles the camera control argument reached to CSIPhy */ int cam_csiphy_core_cfg(void *csiphy_dev, void *arg); /** * @irq_num: IRQ number * @data: CSIPhy device structure * * This API handles CSIPhy IRQs */ irqreturn_t cam_csiphy_irq(int irq_num, void *data); #endif /* _CAM_CSIPHY_CORE_H_ */ drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c 0 → 100644 +239 −0 Original line number Diff line number Diff line /* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * 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. */ #include "cam_csiphy_dev.h" #include "cam_req_mgr_dev.h" #include "cam_csiphy_soc.h" #include "cam_csiphy_core.h" #include <media/cam_sensor.h> #undef CDBG #ifdef CAM_CSIPHY_DEV_DEBUG #define CDBG(fmt, args...) pr_err(fmt, ##args) #else #define CDBG(fmt, args...) pr_debug(fmt, ##args) #endif static long cam_csiphy_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct csiphy_device *csiphy_dev = v4l2_get_subdevdata(sd); int rc = 0; switch (cmd) { case VIDIOC_CAM_CONTROL: rc = cam_csiphy_core_cfg(csiphy_dev, arg); if (rc != 0) { pr_err("%s: %d :ERROR: in configuring the device\n", __func__, __LINE__); return rc; } break; default: pr_err("%s:%d :ERROR: Wrong ioctl\n", __func__, __LINE__); break; } return rc; } #ifdef CONFIG_COMPAT static long cam_csiphy_subdev_compat_ioctl(struct v4l2_subdev *sd, unsigned int cmd, unsigned long arg) { int32_t rc = 0; struct cam_control cmd_data; if (copy_from_user(&cmd_data, (void __user *)arg, sizeof(cmd_data))) { pr_err("Failed to copy from user_ptr=%pK size=%zu\n", (void __user *)arg, sizeof(cmd_data)); return -EFAULT; } /* All the arguments converted to 64 bit here * Passed to the api in core.c */ switch (cmd) { case VIDIOC_CAM_CONTROL: rc = cam_csiphy_subdev_ioctl(sd, cmd, &cmd_data); break; default: pr_err("%s:%d Invalid compat ioctl cmd: %d\n", __func__, __LINE__, cmd); rc = -EINVAL; } if (!rc) { if (copy_to_user((void __user *)arg, &cmd_data, sizeof(cmd_data))) { pr_err("Failed to copy to user_ptr=%pK size=%zu\n", (void __user *)arg, sizeof(cmd_data)); rc = -EFAULT; } } return rc; } #endif static struct v4l2_subdev_core_ops csiphy_subdev_core_ops = { .ioctl = cam_csiphy_subdev_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl32 = cam_csiphy_subdev_compat_ioctl, #endif }; static const struct v4l2_subdev_ops csiphy_subdev_ops = { .core = &csiphy_subdev_core_ops, }; static const struct v4l2_subdev_internal_ops csiphy_subdev_intern_ops; static int32_t cam_csiphy_platform_probe(struct platform_device *pdev) { struct cam_cpas_register_params cpas_parms; struct csiphy_device *new_csiphy_dev; int32_t rc = 0; new_csiphy_dev = devm_kzalloc(&pdev->dev, sizeof(struct csiphy_device), GFP_KERNEL); if (!new_csiphy_dev) return -ENOMEM; new_csiphy_dev->ctrl_reg = kzalloc(sizeof(struct csiphy_ctrl_t), GFP_KERNEL); if (!new_csiphy_dev->ctrl_reg) { devm_kfree(&pdev->dev, new_csiphy_dev); return -ENOMEM; } mutex_init(&new_csiphy_dev->mutex); new_csiphy_dev->v4l2_dev_str.pdev = pdev; new_csiphy_dev->ref_count = 0; rc = cam_csiphy_parse_dt_info(pdev, new_csiphy_dev); if (rc < 0) { pr_err("%s:%d :ERROR: dt paring failed: %d\n", __func__, __LINE__, rc); goto csiphy_no_resource; } new_csiphy_dev->v4l2_dev_str.internal_ops = &csiphy_subdev_intern_ops; new_csiphy_dev->v4l2_dev_str.ops = &csiphy_subdev_ops; strlcpy(new_csiphy_dev->device_name, CAMX_CSIPHY_DEV_NAME, sizeof(new_csiphy_dev->device_name)); new_csiphy_dev->v4l2_dev_str.name = new_csiphy_dev->device_name; new_csiphy_dev->v4l2_dev_str.sd_flags = (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS); new_csiphy_dev->v4l2_dev_str.ent_function = CAM_CSIPHY_DEVICE_TYPE; new_csiphy_dev->v4l2_dev_str.token = new_csiphy_dev; rc = cam_register_subdev(&(new_csiphy_dev->v4l2_dev_str)); if (rc < 0) { pr_err("%s:%d :ERROR: In cam_register_subdev\n", __func__, __LINE__); goto csiphy_no_resource; } platform_set_drvdata(pdev, &(new_csiphy_dev->v4l2_dev_str.sd)); v4l2_set_subdevdata(&(new_csiphy_dev->v4l2_dev_str.sd), new_csiphy_dev); new_csiphy_dev->bridge_intf.device_hdl[0] = -1; new_csiphy_dev->bridge_intf.device_hdl[1] = -1; new_csiphy_dev->bridge_intf.ops.get_dev_info = NULL; new_csiphy_dev->bridge_intf.ops.link_setup = NULL; new_csiphy_dev->bridge_intf.ops.apply_req = NULL; new_csiphy_dev->acquire_count = 0; new_csiphy_dev->is_acquired_dev_combo_mode = 0; cpas_parms.cam_cpas_client_cb = NULL; cpas_parms.cell_index = pdev->id; cpas_parms.dev = &pdev->dev; cpas_parms.userdata = new_csiphy_dev; strlcpy(cpas_parms.identifier, "csiphy", CAM_HW_IDENTIFIER_LENGTH); rc = cam_cpas_register_client(&cpas_parms); if (rc) { pr_err("%s:%d CPAS registration failed\n", __func__, __LINE__); goto csiphy_no_resource; } CDBG("CPAS registration successful handle=%d\n", cpas_parms.client_handle); new_csiphy_dev->cpas_handle = cpas_parms.client_handle; return rc; csiphy_no_resource: mutex_destroy(&new_csiphy_dev->mutex); kfree(new_csiphy_dev->ctrl_reg); devm_kfree(&pdev->dev, new_csiphy_dev); return rc; } static int32_t cam_csiphy_device_remove(struct platform_device *pdev) { struct v4l2_subdev *subdev = platform_get_drvdata(pdev); struct csiphy_device *csiphy_dev = v4l2_get_subdevdata(subdev); cam_cpas_unregister_client(csiphy_dev->cpas_handle); cam_csiphy_soc_release(csiphy_dev); kfree(csiphy_dev->ctrl_reg); devm_kfree(&pdev->dev, csiphy_dev); return 0; } static const struct of_device_id cam_csiphy_dt_match[] = { {.compatible = "qcom,csiphy"}, {} }; MODULE_DEVICE_TABLE(of, cam_csiphy_dt_match); static struct platform_driver csiphy_driver = { .probe = cam_csiphy_platform_probe, .remove = cam_csiphy_device_remove, .driver = { .name = CAMX_CSIPHY_DEV_NAME, .owner = THIS_MODULE, .of_match_table = cam_csiphy_dt_match, }, }; static int32_t __init cam_csiphy_init_module(void) { return platform_driver_register(&csiphy_driver); } static void __exit cam_csiphy_exit_module(void) { platform_driver_unregister(&csiphy_driver); } module_init(cam_csiphy_init_module); module_exit(cam_csiphy_exit_module); MODULE_DESCRIPTION("CAM CSIPHY driver"); MODULE_LICENSE("GPL v2"); Loading
drivers/media/platform/msm/camera/cam_sensor_module/Makefile +1 −0 Original line number Diff line number Diff line obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_utils/ obj-$(CONFIG_SPECTRA_CAMERA) += cam_cci/ obj-$(CONFIG_SPECTRA_CAMERA) += cam_sensor_io/ obj-$(CONFIG_SPECTRA_CAMERA) += cam_csiphy/
drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/Makefile 0 → 100644 +8 −0 Original line number Diff line number Diff line ccflags-y += -Idrivers/media/platform/msm/camera/cam_utils ccflags-y += -Idrivers/media/platform/msm/camera/cam_cpas/include ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_io ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_sensor_utils ccflags-y += -Idrivers/media/platform/msm/camera/cam_sensor_module/cam_cci ccflags-y += -Idrivers/media/platform/msm/camera/cam_req_mgr obj-$(CONFIG_SPECTRA_CAMERA) += cam_csiphy_soc.o cam_csiphy_dev.o cam_csiphy_core.o
drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.c 0 → 100644 +500 −0 Original line number Diff line number Diff line /* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * 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. */ #include <linux/module.h> #include "cam_csiphy_core.h" #include "cam_csiphy_dev.h" #include "cam_csiphy_soc.h" #include <cam_mem_mgr.h> void cam_csiphy_query_cap(struct csiphy_device *csiphy_dev, struct cam_csiphy_query_cap *csiphy_cap) { csiphy_cap->slot_info = csiphy_dev->v4l2_dev_str.pdev->id; csiphy_cap->version = csiphy_dev->hw_version; csiphy_cap->clk_lane = csiphy_dev->clk_lane; } void cam_csiphy_reset(struct csiphy_device *csiphy_dev) { int32_t i; uint32_t size = csiphy_dev->ctrl_reg->csiphy_reg.csiphy_reset_array_size; for (i = 0; i < size; i++) { cam_io_w( csiphy_dev->ctrl_reg-> csiphy_reset_reg[i].reg_data, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_reset_reg[i].reg_addr); usleep_range(csiphy_dev->ctrl_reg-> csiphy_reset_reg[i].delay * 100, csiphy_dev->ctrl_reg-> csiphy_reset_reg[i].delay * 100 + 1000); } } int32_t cam_cmd_buf_parser(struct csiphy_device *csiphy_dev, struct cam_config_dev_cmd *cfg_dev) { int32_t rc = 0; uint64_t generic_ptr; struct cam_packet *csl_packet = NULL; struct cam_cmd_buf_desc *cmd_desc = NULL; uint32_t *cmd_buf = NULL; struct cam_csiphy_info *cam_cmd_csiphy_info = NULL; size_t len; if (!cfg_dev || !csiphy_dev) { pr_err("%s:%d Invalid Args\n", __func__, __LINE__); return -EINVAL; } csiphy_dev->csiphy_info = kzalloc(sizeof(struct cam_csiphy_info), GFP_KERNEL); if (!csiphy_dev->csiphy_info) return -ENOMEM; rc = cam_mem_get_cpu_buf((int32_t) cfg_dev->packet_handle, (uint64_t *)&generic_ptr, &len); if (rc < 0) { pr_err("%s:%d :ERROR: Failed to get packet Mem address: %d\n", __func__, __LINE__, rc); kfree(csiphy_dev->csiphy_info); csiphy_dev->csiphy_info = NULL; return rc; } if (cfg_dev->offset > len) { pr_err("%s: %d offset is out of bounds: offset: %lld len: %zu\n", __func__, __LINE__, cfg_dev->offset, len); kfree(csiphy_dev->csiphy_info); csiphy_dev->csiphy_info = NULL; return -EINVAL; } csl_packet = (struct cam_packet *)(generic_ptr + cfg_dev->offset); cmd_desc = (struct cam_cmd_buf_desc *) ((uint32_t *)&csl_packet->payload + csl_packet->cmd_buf_offset / 4); rc = cam_mem_get_cpu_buf(cmd_desc->mem_handle, (uint64_t *)&generic_ptr, &len); if (rc < 0) { pr_err("%s:%d :ERROR: Failed to get cmd buf Mem address : %d\n", __func__, __LINE__, rc); kfree(csiphy_dev->csiphy_info); csiphy_dev->csiphy_info = NULL; return rc; } cmd_buf = (uint32_t *)generic_ptr; cmd_buf += cmd_desc->offset / 4; cam_cmd_csiphy_info = (struct cam_csiphy_info *)cmd_buf; csiphy_dev->csiphy_info->lane_cnt = cam_cmd_csiphy_info->lane_cnt; csiphy_dev->csiphy_info->lane_mask = cam_cmd_csiphy_info->lane_mask; csiphy_dev->csiphy_info->csiphy_3phase = cam_cmd_csiphy_info->csiphy_3phase; csiphy_dev->csiphy_info->combo_mode = cam_cmd_csiphy_info->combo_mode; csiphy_dev->csiphy_info->settle_time = cam_cmd_csiphy_info->settle_time; csiphy_dev->csiphy_info->data_rate = cam_cmd_csiphy_info->data_rate; return rc; } void cam_csiphy_cphy_irq_config(struct csiphy_device *csiphy_dev) { int32_t i; for (i = 0; i < csiphy_dev->num_irq_registers; i++) cam_io_w(csiphy_dev->ctrl_reg-> csiphy_irq_reg[i].reg_data, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_irq_reg[i].reg_addr); } void cam_csiphy_cphy_irq_disable(struct csiphy_device *csiphy_dev) { int32_t i; for (i = 0; i < csiphy_dev->num_irq_registers; i++) cam_io_w(0x0, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_irq_reg[i].reg_addr); } irqreturn_t cam_csiphy_irq(int irq_num, void *data) { uint32_t irq; uint8_t i; struct csiphy_device *csiphy_dev = (struct csiphy_device *)data; if (!csiphy_dev) { pr_err("%s:%d Invalid Args\n", __func__, __LINE__); return -EINVAL; } for (i = 0; i < csiphy_dev->num_irq_registers; i++) { irq = cam_io_r( csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_reg. mipi_csiphy_interrupt_status0_addr + 0x4*i); cam_io_w(irq, csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_reg. mipi_csiphy_interrupt_clear0_addr + 0x4*i); pr_err_ratelimited( "%s CSIPHY%d_IRQ_STATUS_ADDR%d = 0x%x\n", __func__, csiphy_dev->v4l2_dev_str.pdev->id, i, irq); cam_io_w(0x0, csiphy_dev->base + csiphy_dev->ctrl_reg->csiphy_reg. mipi_csiphy_interrupt_clear0_addr + 0x4*i); } cam_io_w(0x1, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_reg.mipi_csiphy_glbl_irq_cmd_addr); cam_io_w(0x0, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_reg.mipi_csiphy_glbl_irq_cmd_addr); return IRQ_HANDLED; } int32_t cam_csiphy_config_dev(struct csiphy_device *csiphy_dev) { int32_t rc = 0; uint32_t lane_enable = 0, mask = 1, size = 0; uint16_t lane_mask = 0, i = 0, cfg_size = 0; uint8_t settle_cnt, lane_cnt, lane_pos = 0; void __iomem *csiphybase; struct csiphy_reg_t (*reg_array)[MAX_SETTINGS_PER_LANE]; if (csiphy_dev->csiphy_info == NULL) { pr_err("%s:%d csiphy_info is NULL, No/Fail CONFIG_DEV ?\n", __func__, __LINE__); return -EINVAL; } lane_cnt = csiphy_dev->csiphy_info->lane_cnt; lane_mask = csiphy_dev->csiphy_info->lane_mask & 0x1f; settle_cnt = (csiphy_dev->csiphy_info->settle_time / 200000000); csiphybase = csiphy_dev->base; if (!csiphybase) { pr_err("%s: csiphybase NULL\n", __func__); return -EINVAL; } for (i = 0; i < MAX_DPHY_DATA_LN; i++) { if (mask == 0x2) { if (lane_mask & mask) lane_enable |= 0x80; i--; } else if (lane_mask & mask) { lane_enable |= 0x1 << (i<<1); } mask <<= 1; } if (!csiphy_dev->csiphy_info->csiphy_3phase) { if (csiphy_dev->csiphy_info->combo_mode == 1) reg_array = csiphy_dev->ctrl_reg->csiphy_2ph_combo_mode_reg; else reg_array = csiphy_dev->ctrl_reg->csiphy_2ph_reg; csiphy_dev->num_irq_registers = 11; cfg_size = csiphy_dev->ctrl_reg->csiphy_reg. csiphy_2ph_config_array_size; } else { if (csiphy_dev->csiphy_info->combo_mode == 1) reg_array = csiphy_dev->ctrl_reg->csiphy_2ph_3ph_mode_reg; else reg_array = csiphy_dev->ctrl_reg->csiphy_3ph_reg; csiphy_dev->num_irq_registers = 20; cfg_size = csiphy_dev->ctrl_reg->csiphy_reg. csiphy_3ph_config_array_size; } size = csiphy_dev->ctrl_reg->csiphy_reg.csiphy_common_array_size; for (i = 0; i < size; i++) { switch (csiphy_dev->ctrl_reg-> csiphy_common_reg[i].csiphy_param_type) { case CSIPHY_LANE_ENABLE: cam_io_w(lane_enable, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_common_reg[i].reg_addr); break; case CSIPHY_DEFAULT_PARAMS: cam_io_w(csiphy_dev->ctrl_reg-> csiphy_common_reg[i].reg_data, csiphy_dev->base + csiphy_dev->ctrl_reg-> csiphy_common_reg[i].reg_addr); break; default: break; } } while (lane_mask & 0x1f) { if (!(lane_mask & 0x1)) { lane_pos++; lane_mask >>= 1; continue; } for (i = 0; i < cfg_size; i++) { switch (reg_array[lane_pos][i].csiphy_param_type) { case CSIPHY_LANE_ENABLE: cam_io_w(lane_enable, csiphy_dev->base + reg_array[lane_pos][i].reg_addr); break; case CSIPHY_DEFAULT_PARAMS: cam_io_w(reg_array[lane_pos][i].reg_data, csiphy_dev->base + reg_array[lane_pos][i].reg_addr); break; case CSIPHY_SETTLE_CNT_LOWER_BYTE: cam_io_w(settle_cnt & 0xFF, csiphy_dev->base + reg_array[lane_pos][i].reg_addr); break; case CSIPHY_SETTLE_CNT_HIGHER_BYTE: cam_io_w((settle_cnt >> 8) & 0xFF, csiphy_dev->base + reg_array[lane_pos][i].reg_addr); break; default: CDBG("%s: %d Do Nothing\n", __func__, __LINE__); break; } usleep_range(reg_array[lane_pos][i].delay*1000, reg_array[lane_pos][i].delay*1000 + 1000); } lane_mask >>= 1; lane_pos++; } cam_csiphy_cphy_irq_config(csiphy_dev); return rc; } int32_t cam_csiphy_core_cfg(void *phy_dev, void *arg) { struct csiphy_device *csiphy_dev = (struct csiphy_device *)phy_dev; struct cam_control *cmd = (struct cam_control *)arg; int32_t rc = 0; if (!csiphy_dev || !cmd) { pr_err("%s:%d Invalid input args\n", __func__, __LINE__); return -EINVAL; } pr_debug("%s:%d Opcode received: %d\n", __func__, __LINE__, cmd->op_code); mutex_lock(&csiphy_dev->mutex); switch (cmd->op_code) { case CAM_ACQUIRE_DEV: { struct cam_sensor_acquire_dev csiphy_acq_dev; struct cam_csiphy_acquire_dev_info csiphy_acq_params; struct cam_create_dev_hdl bridge_params; rc = copy_from_user(&csiphy_acq_dev, (void __user *)cmd->handle, sizeof(csiphy_acq_dev)); if (rc < 0) { pr_err("%s:%d :ERROR: Failed copying from User\n", __func__, __LINE__); goto release_mutex; } csiphy_acq_params.combo_mode = 0; if (csiphy_dev->acquire_count == 2) { pr_err("%s:%d CSIPHY device do not allow more than 2 acquires\n", __func__, __LINE__); rc = -EINVAL; goto release_mutex; } bridge_params.ops = NULL; bridge_params.session_hdl = csiphy_acq_dev.session_handle; bridge_params.v4l2_sub_dev_flag = 0; bridge_params.media_entity_flag = 0; bridge_params.priv = csiphy_dev; csiphy_acq_dev.device_handle = cam_create_device_hdl(&bridge_params); csiphy_dev->bridge_intf. device_hdl[csiphy_acq_params.combo_mode] = csiphy_acq_dev.device_handle; csiphy_dev->bridge_intf. session_hdl[csiphy_acq_params.combo_mode] = csiphy_acq_dev.session_handle; if (copy_to_user((void __user *)cmd->handle, &csiphy_acq_dev, sizeof(struct cam_sensor_acquire_dev))) { pr_err("%s:%d :ERROR: Failed copying from User\n", __func__, __LINE__); rc = -EINVAL; goto release_mutex; } if (csiphy_acq_params.combo_mode == 1) csiphy_dev->is_acquired_dev_combo_mode = 1; csiphy_dev->acquire_count++; } break; case CAM_QUERY_CAP: { struct cam_csiphy_query_cap csiphy_cap; cam_csiphy_query_cap(csiphy_dev, &csiphy_cap); if (copy_to_user((void __user *)cmd->handle, &csiphy_cap, sizeof(struct cam_csiphy_query_cap))) { pr_err("%s:%d :ERROR: Failed copying from User\n", __func__, __LINE__); rc = -EINVAL; goto release_mutex; } } break; case CAM_STOP_DEV: { rc = cam_csiphy_soc_release(csiphy_dev); if (rc < 0) { pr_err("%s:%d Failed in csiphy release\n", __func__, __LINE__); cam_cpas_stop(csiphy_dev->cpas_handle); goto release_mutex; } rc = cam_cpas_stop(csiphy_dev->cpas_handle); if (rc < 0) { pr_err("%s:%d :Error: de-voting CPAS: %d\n", __func__, __LINE__, rc); goto release_mutex; } } break; case CAM_RELEASE_DEV: { struct cam_release_dev_cmd release; if (!csiphy_dev->acquire_count) { pr_err("%s:%d No valid devices to release\n", __func__, __LINE__); rc = -EINVAL; goto release_mutex; } if (copy_from_user(&release, (void __user *) cmd->handle, sizeof(release))) { rc = -EFAULT; goto release_mutex; } rc = cam_destroy_device_hdl(release.dev_handle); if (rc < 0) pr_err("%s:%d :ERROR: destroying the device hdl\n", __func__, __LINE__); if (release.dev_handle == csiphy_dev->bridge_intf.device_hdl[0]) { csiphy_dev->bridge_intf.device_hdl[0] = -1; csiphy_dev->bridge_intf.link_hdl[0] = -1; csiphy_dev->bridge_intf.session_hdl[0] = -1; } else { csiphy_dev->bridge_intf.device_hdl[1] = -1; csiphy_dev->bridge_intf.link_hdl[1] = -1; csiphy_dev->bridge_intf. session_hdl[1] = -1; } csiphy_dev->acquire_count--; } break; case CAM_CONFIG_DEV: { struct cam_config_dev_cmd config; if (copy_from_user(&config, (void __user *)cmd->handle, sizeof(config))) { rc = -EFAULT; } else { rc = cam_cmd_buf_parser(csiphy_dev, &config); if (rc < 0) { pr_err("%s:%d Fail in cmd buf parser\n", __func__, __LINE__); goto release_mutex; } } break; } case CAM_START_DEV: { struct cam_ahb_vote ahb_vote; struct cam_axi_vote axi_vote; ahb_vote.type = CAM_VOTE_ABSOLUTE; ahb_vote.vote.level = CAM_SVS_VOTE; axi_vote.compressed_bw = CAM_CPAS_DEFAULT_AXI_BW; axi_vote.uncompressed_bw = CAM_CPAS_DEFAULT_AXI_BW; rc = cam_cpas_start(csiphy_dev->cpas_handle, &ahb_vote, &axi_vote); if (rc < 0) { pr_err("%s:%d :Error: voting CPAS: %d\n", __func__, __LINE__, rc); goto release_mutex; } rc = cam_csiphy_enable_hw(csiphy_dev); if (rc != 0) { pr_err("%s: %d cam_csiphy_enable_hw failed\n", __func__, __LINE__); cam_cpas_stop(csiphy_dev->cpas_handle); goto release_mutex; } rc = cam_csiphy_config_dev(csiphy_dev); if (rc < 0) { pr_err("%s: %d cam_csiphy_config_dev failed\n", __func__, __LINE__); cam_cpas_stop(csiphy_dev->cpas_handle); goto release_mutex; } } break; default: pr_err("%s:%d :Error: Invalid Opcode: %d\n", __func__, __LINE__, cmd->op_code); rc = -EINVAL; goto release_mutex; } release_mutex: mutex_unlock(&csiphy_dev->mutex); return rc; }
drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_core.h 0 → 100644 +52 −0 Original line number Diff line number Diff line /* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * 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. */ #ifndef _CAM_CSIPHY_CORE_H_ #define _CAM_CSIPHY_CORE_H_ #include <linux/irqreturn.h> #include "cam_csiphy_dev.h" #include <cam_mem_mgr.h> #include <cam_req_mgr_util.h> #include <cam_io_util.h> /** * @csiphy_dev: CSIPhy device structure * * This API programs CSIPhy IRQ registers */ void cam_csiphy_cphy_irq_config(struct csiphy_device *csiphy_dev); /** * @csiphy_dev: CSIPhy device structure * * This API resets CSIPhy hardware */ void cam_csiphy_reset(struct csiphy_device *csiphy_dev); /** * @csiphy_dev: CSIPhy device structure * @arg: Camera control command argument * * This API handles the camera control argument reached to CSIPhy */ int cam_csiphy_core_cfg(void *csiphy_dev, void *arg); /** * @irq_num: IRQ number * @data: CSIPhy device structure * * This API handles CSIPhy IRQs */ irqreturn_t cam_csiphy_irq(int irq_num, void *data); #endif /* _CAM_CSIPHY_CORE_H_ */
drivers/media/platform/msm/camera/cam_sensor_module/cam_csiphy/cam_csiphy_dev.c 0 → 100644 +239 −0 Original line number Diff line number Diff line /* Copyright (c) 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * 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. */ #include "cam_csiphy_dev.h" #include "cam_req_mgr_dev.h" #include "cam_csiphy_soc.h" #include "cam_csiphy_core.h" #include <media/cam_sensor.h> #undef CDBG #ifdef CAM_CSIPHY_DEV_DEBUG #define CDBG(fmt, args...) pr_err(fmt, ##args) #else #define CDBG(fmt, args...) pr_debug(fmt, ##args) #endif static long cam_csiphy_subdev_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct csiphy_device *csiphy_dev = v4l2_get_subdevdata(sd); int rc = 0; switch (cmd) { case VIDIOC_CAM_CONTROL: rc = cam_csiphy_core_cfg(csiphy_dev, arg); if (rc != 0) { pr_err("%s: %d :ERROR: in configuring the device\n", __func__, __LINE__); return rc; } break; default: pr_err("%s:%d :ERROR: Wrong ioctl\n", __func__, __LINE__); break; } return rc; } #ifdef CONFIG_COMPAT static long cam_csiphy_subdev_compat_ioctl(struct v4l2_subdev *sd, unsigned int cmd, unsigned long arg) { int32_t rc = 0; struct cam_control cmd_data; if (copy_from_user(&cmd_data, (void __user *)arg, sizeof(cmd_data))) { pr_err("Failed to copy from user_ptr=%pK size=%zu\n", (void __user *)arg, sizeof(cmd_data)); return -EFAULT; } /* All the arguments converted to 64 bit here * Passed to the api in core.c */ switch (cmd) { case VIDIOC_CAM_CONTROL: rc = cam_csiphy_subdev_ioctl(sd, cmd, &cmd_data); break; default: pr_err("%s:%d Invalid compat ioctl cmd: %d\n", __func__, __LINE__, cmd); rc = -EINVAL; } if (!rc) { if (copy_to_user((void __user *)arg, &cmd_data, sizeof(cmd_data))) { pr_err("Failed to copy to user_ptr=%pK size=%zu\n", (void __user *)arg, sizeof(cmd_data)); rc = -EFAULT; } } return rc; } #endif static struct v4l2_subdev_core_ops csiphy_subdev_core_ops = { .ioctl = cam_csiphy_subdev_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl32 = cam_csiphy_subdev_compat_ioctl, #endif }; static const struct v4l2_subdev_ops csiphy_subdev_ops = { .core = &csiphy_subdev_core_ops, }; static const struct v4l2_subdev_internal_ops csiphy_subdev_intern_ops; static int32_t cam_csiphy_platform_probe(struct platform_device *pdev) { struct cam_cpas_register_params cpas_parms; struct csiphy_device *new_csiphy_dev; int32_t rc = 0; new_csiphy_dev = devm_kzalloc(&pdev->dev, sizeof(struct csiphy_device), GFP_KERNEL); if (!new_csiphy_dev) return -ENOMEM; new_csiphy_dev->ctrl_reg = kzalloc(sizeof(struct csiphy_ctrl_t), GFP_KERNEL); if (!new_csiphy_dev->ctrl_reg) { devm_kfree(&pdev->dev, new_csiphy_dev); return -ENOMEM; } mutex_init(&new_csiphy_dev->mutex); new_csiphy_dev->v4l2_dev_str.pdev = pdev; new_csiphy_dev->ref_count = 0; rc = cam_csiphy_parse_dt_info(pdev, new_csiphy_dev); if (rc < 0) { pr_err("%s:%d :ERROR: dt paring failed: %d\n", __func__, __LINE__, rc); goto csiphy_no_resource; } new_csiphy_dev->v4l2_dev_str.internal_ops = &csiphy_subdev_intern_ops; new_csiphy_dev->v4l2_dev_str.ops = &csiphy_subdev_ops; strlcpy(new_csiphy_dev->device_name, CAMX_CSIPHY_DEV_NAME, sizeof(new_csiphy_dev->device_name)); new_csiphy_dev->v4l2_dev_str.name = new_csiphy_dev->device_name; new_csiphy_dev->v4l2_dev_str.sd_flags = (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS); new_csiphy_dev->v4l2_dev_str.ent_function = CAM_CSIPHY_DEVICE_TYPE; new_csiphy_dev->v4l2_dev_str.token = new_csiphy_dev; rc = cam_register_subdev(&(new_csiphy_dev->v4l2_dev_str)); if (rc < 0) { pr_err("%s:%d :ERROR: In cam_register_subdev\n", __func__, __LINE__); goto csiphy_no_resource; } platform_set_drvdata(pdev, &(new_csiphy_dev->v4l2_dev_str.sd)); v4l2_set_subdevdata(&(new_csiphy_dev->v4l2_dev_str.sd), new_csiphy_dev); new_csiphy_dev->bridge_intf.device_hdl[0] = -1; new_csiphy_dev->bridge_intf.device_hdl[1] = -1; new_csiphy_dev->bridge_intf.ops.get_dev_info = NULL; new_csiphy_dev->bridge_intf.ops.link_setup = NULL; new_csiphy_dev->bridge_intf.ops.apply_req = NULL; new_csiphy_dev->acquire_count = 0; new_csiphy_dev->is_acquired_dev_combo_mode = 0; cpas_parms.cam_cpas_client_cb = NULL; cpas_parms.cell_index = pdev->id; cpas_parms.dev = &pdev->dev; cpas_parms.userdata = new_csiphy_dev; strlcpy(cpas_parms.identifier, "csiphy", CAM_HW_IDENTIFIER_LENGTH); rc = cam_cpas_register_client(&cpas_parms); if (rc) { pr_err("%s:%d CPAS registration failed\n", __func__, __LINE__); goto csiphy_no_resource; } CDBG("CPAS registration successful handle=%d\n", cpas_parms.client_handle); new_csiphy_dev->cpas_handle = cpas_parms.client_handle; return rc; csiphy_no_resource: mutex_destroy(&new_csiphy_dev->mutex); kfree(new_csiphy_dev->ctrl_reg); devm_kfree(&pdev->dev, new_csiphy_dev); return rc; } static int32_t cam_csiphy_device_remove(struct platform_device *pdev) { struct v4l2_subdev *subdev = platform_get_drvdata(pdev); struct csiphy_device *csiphy_dev = v4l2_get_subdevdata(subdev); cam_cpas_unregister_client(csiphy_dev->cpas_handle); cam_csiphy_soc_release(csiphy_dev); kfree(csiphy_dev->ctrl_reg); devm_kfree(&pdev->dev, csiphy_dev); return 0; } static const struct of_device_id cam_csiphy_dt_match[] = { {.compatible = "qcom,csiphy"}, {} }; MODULE_DEVICE_TABLE(of, cam_csiphy_dt_match); static struct platform_driver csiphy_driver = { .probe = cam_csiphy_platform_probe, .remove = cam_csiphy_device_remove, .driver = { .name = CAMX_CSIPHY_DEV_NAME, .owner = THIS_MODULE, .of_match_table = cam_csiphy_dt_match, }, }; static int32_t __init cam_csiphy_init_module(void) { return platform_driver_register(&csiphy_driver); } static void __exit cam_csiphy_exit_module(void) { platform_driver_unregister(&csiphy_driver); } module_init(cam_csiphy_init_module); module_exit(cam_csiphy_exit_module); MODULE_DESCRIPTION("CAM CSIPHY driver"); MODULE_LICENSE("GPL v2");