Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5262c11 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: camera: Add support for CSIPHY driver" into msm-4.9

parents 4cd84f1d 92e825b1
Loading
Loading
Loading
Loading
+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/
+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
+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;
}
+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_ */
+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