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

Commit 83e46a81 authored by Anil Kumar Kanakanti's avatar Anil Kumar Kanakanti
Browse files

msm: camera: cci: Add check for the cci master initialization



Multiple Sensor submodules can stream on same CCI master. Currently,
there is not any knowledge, whether requested master is already
initialized by other submodules. This can result in operation slave
in error, while other submodule tries to init the same master.
This change adds the boolean variable to notify cci hardware, whether
master is already initialized, and perform the operations accordingly.

Change-Id: I5fe740cb61d47145b1b63161a1c1ef36cf25187a
CRs-Fixed: 2782553
Signed-off-by: default avatarAnil Kumar Kanakanti <akanakan@codeaurora.org>
parent cc31ba39
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ static int32_t cam_cci_convert_type_to_num_bytes(
		num_bytes = 4;
		break;
	default:
		CAM_ERR(CAM_CCI, "failed: %d", type);
		CAM_ERR(CAM_CCI, "Wrong Sensor I2c Type: %d", type);
		num_bytes = 0;
		break;
	}
@@ -1648,14 +1648,15 @@ static int32_t cam_cci_i2c_set_sync_prms(struct v4l2_subdev *sd,
	return rc;
}

static int32_t cam_cci_release(struct v4l2_subdev *sd)
static int32_t cam_cci_release(struct v4l2_subdev *sd,
	enum cci_i2c_master_t master)
{
	uint8_t rc = 0;
	struct cci_device *cci_dev;

	cci_dev = v4l2_get_subdevdata(sd);

	rc = cam_cci_soc_release(cci_dev);
	rc = cam_cci_soc_release(cci_dev, master);
	if (rc < 0) {
		CAM_ERR(CAM_CCI, "Failed in releasing the cci: %d", rc);
		return rc;
@@ -1766,7 +1767,7 @@ int32_t cam_cci_core_cfg(struct v4l2_subdev *sd,
		break;
	case MSM_CCI_RELEASE:
		mutex_lock(&cci_dev->init_mutex);
		rc = cam_cci_release(sd);
		rc = cam_cci_release(sd, master);
		mutex_unlock(&cci_dev->init_mutex);
		break;
	case MSM_CCI_I2C_READ:
+7 −5
Original line number Diff line number Diff line
@@ -39,7 +39,6 @@

#define CCI_TIMEOUT msecs_to_jiffies(1500)

#define NUM_MASTERS 2
#define NUM_QUEUES 2

#define CCI_PINCTRL_STATE_DEFAULT "cci_default"
@@ -122,7 +121,7 @@ struct cam_cci_i2c_queue_info {
};

struct cam_cci_master_info {
	uint32_t status;
	int32_t status;
	atomic_t q_free[NUM_QUEUES];
	uint8_t q_lock[NUM_QUEUES];
	uint8_t reset_pending;
@@ -138,6 +137,7 @@ struct cam_cci_master_info {
	struct semaphore master_sem;
	bool is_first_req;
	uint16_t freq_ref_cnt;
	bool is_initilized;
};

struct cam_cci_clk_params_t {
@@ -172,6 +172,7 @@ enum cam_cci_state_t {
 * @cci_clk_info:               CCI clock information
 * @cam_cci_i2c_queue_info:     CCI queue information
 * @i2c_freq_mode:              I2C frequency of operations
 * @master_active_slave:        Number of active/connected slaves for master
 * @cci_clk_params:             CCI hw clk params
 * @cci_gpio_tbl:               CCI GPIO table
 * @cci_gpio_tbl_size:          GPIO table size
@@ -204,9 +205,10 @@ struct cci_device {
	uint8_t ref_count;
	enum cam_cci_state_t cci_state;
	struct cam_cci_i2c_queue_info
		cci_i2c_queue_info[NUM_MASTERS][NUM_QUEUES];
	struct cam_cci_master_info cci_master_info[NUM_MASTERS];
	enum i2c_freq_mode i2c_freq_mode[NUM_MASTERS];
		cci_i2c_queue_info[MASTER_MAX][NUM_QUEUES];
	struct cam_cci_master_info cci_master_info[MASTER_MAX];
	enum i2c_freq_mode i2c_freq_mode[MASTER_MAX];
	uint8_t master_active_slave[MASTER_MAX];
	struct cam_cci_clk_params_t cci_clk_params[I2C_MAX_MODES];
	struct msm_pinctrl_info cci_pinctrl;
	uint8_t cci_pinctrl_status;
+144 −123
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
 */

#include "cam_cci_dev.h"
#include "cam_cci_core.h"

static int cam_cci_init_master(struct cci_device *cci_dev,
	enum cci_i2c_master_t master)
{
	int i = 0, rc = 0;
	void __iomem *base = NULL;
	struct cam_hw_soc_info *soc_info = NULL;
	uint32_t max_queue_0_size = 0, max_queue_1_size = 0;

	soc_info = &cci_dev->soc_info;
	base = soc_info->reg_map[0].mem_base;

	max_queue_0_size = CCI_I2C_QUEUE_0_SIZE;
	max_queue_1_size = CCI_I2C_QUEUE_1_SIZE;

	cci_dev->master_active_slave[master]++;
	if (!cci_dev->cci_master_info[master].is_initilized) {
		/* Re-initialize the completion */
		reinit_completion(
		&cci_dev->cci_master_info[master].reset_complete);
		reinit_completion(&cci_dev->cci_master_info[master].rd_done);

		/* reinit the reports for the queue */
		for (i = 0; i < NUM_QUEUES; i++)
			reinit_completion(
			&cci_dev->cci_master_info[master].report_q[i]);

		/* Set reset pending flag to true */
		cci_dev->cci_master_info[master].reset_pending = true;
		cci_dev->cci_master_info[master].status = 0;
		if (cci_dev->ref_count == 1) {
			cam_io_w_mb(CCI_RESET_CMD_RMSK,
				base + CCI_RESET_CMD_ADDR);
			cam_io_w_mb(0x1, base + CCI_RESET_CMD_ADDR);
		} else {
			cam_io_w_mb((master == MASTER_0) ?
				CCI_M0_RESET_RMSK : CCI_M1_RESET_RMSK,
				base + CCI_RESET_CMD_ADDR);
		}
		if (!wait_for_completion_timeout(
			&cci_dev->cci_master_info[master].reset_complete,
			CCI_TIMEOUT)) {
			CAM_ERR(CAM_CCI,
				"Failed: reset complete timeout for master: %d",
				master);
			rc = -ETIMEDOUT;
			cci_dev->master_active_slave[master]--;
			return rc;
		}

		flush_workqueue(cci_dev->write_wq[master]);

		/* Setting up the queue size for master */
		cci_dev->cci_i2c_queue_info[master][QUEUE_0].max_queue_size
					= max_queue_0_size;
		cci_dev->cci_i2c_queue_info[master][QUEUE_1].max_queue_size
					= max_queue_1_size;

		CAM_DBG(CAM_CCI, "CCI Master[%d] :: Q0: %d Q1: %d", master,
			cci_dev->cci_i2c_queue_info[master][QUEUE_0]
				.max_queue_size,
			cci_dev->cci_i2c_queue_info[master][QUEUE_1]
				.max_queue_size);

		cci_dev->cci_master_info[master].status = 0;
		cci_dev->cci_master_info[master].is_initilized = true;
	}

	return 0;
}

int cam_cci_init(struct v4l2_subdev *sd,
	struct cam_cci_ctrl *c_ctrl)
{
	uint8_t i = 0, j = 0;
	uint8_t i = 0;
	int32_t rc = 0;
	struct cci_device *cci_dev;
	enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;
@@ -20,7 +90,8 @@ int cam_cci_init(struct v4l2_subdev *sd,

	cci_dev = v4l2_get_subdevdata(sd);
	if (!cci_dev || !c_ctrl) {
		CAM_ERR(CAM_CCI, "failed: invalid params %pK %pK",
		CAM_ERR(CAM_CCI,
			"failed: invalid params cci_dev:%pK, c_ctrl:%pK",
			cci_dev, c_ctrl);
		rc = -EINVAL;
		return rc;
@@ -30,144 +101,83 @@ int cam_cci_init(struct v4l2_subdev *sd,
	base = soc_info->reg_map[0].mem_base;

	if (!soc_info || !base) {
		CAM_ERR(CAM_CCI, "failed: invalid params %pK %pK",
		CAM_ERR(CAM_CCI,
			"failed: invalid params soc_info:%pK, base:%pK",
			soc_info, base);
		rc = -EINVAL;
		return rc;
	}

	CAM_DBG(CAM_CCI, "Base address %pK", base);
	if (master >= MASTER_MAX || master < 0) {
		CAM_ERR(CAM_CCI, "Incorrect Master: %d", master);
		return -EINVAL;
	}

	if (!cci_dev->write_wq[master]) {
		CAM_ERR(CAM_CCI, "Null memory for write wq[:%d]", master);
		rc = -ENOMEM;
		return rc;
	}

	if (cci_dev->ref_count++) {
		CAM_DBG(CAM_CCI, "ref_count %d", cci_dev->ref_count);
		CAM_DBG(CAM_CCI, "master %d", master);
		if (master < MASTER_MAX && master >= 0) {
			mutex_lock(&cci_dev->cci_master_info[master].mutex);
			flush_workqueue(cci_dev->write_wq[master]);
			/* Re-initialize the completion */
			reinit_completion(
			&cci_dev->cci_master_info[master].reset_complete);
			reinit_completion(
			&cci_dev->cci_master_info[master].rd_done);
			for (i = 0; i < NUM_QUEUES; i++)
				reinit_completion(
				&cci_dev->cci_master_info[master].report_q[i]);
			/* Set reset pending flag to true */
			cci_dev->cci_master_info[master].reset_pending = true;
			cci_dev->cci_master_info[master].status = 0;
			/* Set proper mask to RESET CMD address */
			if (master == MASTER_0)
				cam_io_w_mb(CCI_M0_RESET_RMSK,
					base + CCI_RESET_CMD_ADDR);
			else
				cam_io_w_mb(CCI_M1_RESET_RMSK,
					base + CCI_RESET_CMD_ADDR);
			/* wait for reset done irq */
			rc = wait_for_completion_timeout(
			&cci_dev->cci_master_info[master].reset_complete,
				CCI_TIMEOUT);
			if (rc <= 0)
				CAM_ERR(CAM_CCI, "wait failed %d", rc);
			cci_dev->cci_master_info[master].status = 0;
			mutex_unlock(&cci_dev->cci_master_info[master].mutex);
		rc = cam_cci_init_master(cci_dev, master);
		if (rc) {
			CAM_ERR(CAM_CCI, "Failed to init: Master: %d: rc: %d",
				master, rc);
			cci_dev->ref_count--;
		}
		return 0;
		CAM_DBG(CAM_CCI, "ref_count %d, master: %d",
			cci_dev->ref_count, master);
		return rc;
	}

	ahb_vote.type = CAM_VOTE_ABSOLUTE;
	ahb_vote.vote.level = CAM_LOWSVS_VOTE;
	axi_vote.num_paths = 1;
	axi_vote.axi_path[0].path_data_type =
		CAM_AXI_PATH_DATA_ALL;
	axi_vote.axi_path[0].transac_type =
		CAM_AXI_TRANSACTION_WRITE;
	axi_vote.axi_path[0].camnoc_bw =
		CAM_CPAS_DEFAULT_AXI_BW;
	axi_vote.axi_path[0].mnoc_ab_bw =
		CAM_CPAS_DEFAULT_AXI_BW;
	axi_vote.axi_path[0].mnoc_ib_bw =
		CAM_CPAS_DEFAULT_AXI_BW;

	rc = cam_cpas_start(cci_dev->cpas_handle,
		&ahb_vote, &axi_vote);
	if (rc != 0)
		CAM_ERR(CAM_CCI, "CPAS start failed");
	axi_vote.axi_path[0].path_data_type = CAM_AXI_PATH_DATA_ALL;
	axi_vote.axi_path[0].transac_type = CAM_AXI_TRANSACTION_WRITE;
	axi_vote.axi_path[0].camnoc_bw = CAM_CPAS_DEFAULT_AXI_BW;
	axi_vote.axi_path[0].mnoc_ab_bw = CAM_CPAS_DEFAULT_AXI_BW;
	axi_vote.axi_path[0].mnoc_ib_bw = CAM_CPAS_DEFAULT_AXI_BW;

	cam_cci_get_clk_rates(cci_dev, c_ctrl);
	rc = cam_cpas_start(cci_dev->cpas_handle, &ahb_vote, &axi_vote);
	if (rc) {
		CAM_ERR(CAM_CCI, "CPAS start failed rc= %d", rc);
		return rc;
	}

	/* Re-initialize the completion */
	reinit_completion(&cci_dev->cci_master_info[master].reset_complete);
	reinit_completion(&cci_dev->cci_master_info[master].rd_done);
	for (i = 0; i < NUM_QUEUES; i++)
		reinit_completion(
			&cci_dev->cci_master_info[master].report_q[i]);
	cam_cci_get_clk_rates(cci_dev, c_ctrl);

	/* Enable Regulators and IRQ*/
	rc = cam_soc_util_enable_platform_resource(soc_info, true,
		CAM_LOWSVS_VOTE, true);
	if (rc < 0) {
		CAM_DBG(CAM_CCI, "request platform resources failed");
		CAM_DBG(CAM_CCI, "request platform resources failed, rc: %d",
			rc);
		goto platform_enable_failed;
	}

	cci_dev->hw_version = cam_io_r_mb(base +
		CCI_HW_VERSION_ADDR);
	cci_dev->hw_version = cam_io_r_mb(base + CCI_HW_VERSION_ADDR);
	CAM_DBG(CAM_CCI, "hw_version = 0x%x", cci_dev->hw_version);

	cci_dev->payload_size =
		MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11;
	cci_dev->payload_size = MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11;
	cci_dev->support_seq_write = 1;

	for (i = 0; i < NUM_MASTERS; i++) {
		for (j = 0; j < NUM_QUEUES; j++) {
			if (j == QUEUE_0)
				cci_dev->cci_i2c_queue_info[i][j].max_queue_size
					= CCI_I2C_QUEUE_0_SIZE;
			else
				cci_dev->cci_i2c_queue_info[i][j].max_queue_size
					= CCI_I2C_QUEUE_1_SIZE;

			CAM_DBG(CAM_CCI, "CCI Master[%d] :: Q0 : %d Q1 : %d", i,
			cci_dev->cci_i2c_queue_info[i][j].max_queue_size,
			cci_dev->cci_i2c_queue_info[i][j].max_queue_size);
		}
	}

	cci_dev->cci_master_info[master].reset_pending = true;
	cci_dev->cci_master_info[master].status = 0;
	cam_io_w_mb(CCI_RESET_CMD_RMSK, base +
			CCI_RESET_CMD_ADDR);
	cam_io_w_mb(0x1, base + CCI_RESET_CMD_ADDR);
	rc = wait_for_completion_timeout(
		&cci_dev->cci_master_info[master].reset_complete,
		CCI_TIMEOUT);
	if (rc <= 0) {
		CAM_ERR(CAM_CCI, "wait_for_completion_timeout");
		if (rc == 0)
			rc = -ETIMEDOUT;
	rc = cam_cci_init_master(cci_dev, master);
	if (rc) {
		CAM_ERR(CAM_CCI, "Failed to init: Master: %d, rc: %d",
			master, rc);
		goto reset_complete_failed;
	}

	for (i = 0; i < MASTER_MAX; i++)
		cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES;
	cam_io_w_mb(CCI_IRQ_MASK_0_RMSK,
		base + CCI_IRQ_MASK_0_ADDR);
	cam_io_w_mb(CCI_IRQ_MASK_0_RMSK,
		base + CCI_IRQ_CLEAR_0_ADDR);
	cam_io_w_mb(CCI_IRQ_MASK_1_RMSK,
		base + CCI_IRQ_MASK_1_ADDR);
	cam_io_w_mb(CCI_IRQ_MASK_1_RMSK,
		base + CCI_IRQ_CLEAR_1_ADDR);
	cam_io_w_mb(0x1, base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);

	for (i = 0; i < MASTER_MAX; i++) {
		if (!cci_dev->write_wq[i]) {
			CAM_ERR(CAM_CCI, "Failed to flush write wq");
			rc = -ENOMEM;
			goto reset_complete_failed;
		} else {
			flush_workqueue(cci_dev->write_wq[i]);
		}
	}
	cam_io_w_mb(CCI_IRQ_MASK_0_RMSK, base + CCI_IRQ_MASK_0_ADDR);
	cam_io_w_mb(CCI_IRQ_MASK_0_RMSK, base + CCI_IRQ_CLEAR_0_ADDR);
	cam_io_w_mb(CCI_IRQ_MASK_1_RMSK, base + CCI_IRQ_MASK_1_ADDR);
	cam_io_w_mb(CCI_IRQ_MASK_1_RMSK, base + CCI_IRQ_CLEAR_1_ADDR);
	cam_io_w_mb(0x1, base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);

	/* Set RD FIFO threshold for M0 & M1 */
	cam_io_w_mb(CCI_I2C_RD_THRESHOLD_VALUE,
@@ -181,7 +191,6 @@ int cam_cci_init(struct v4l2_subdev *sd,

reset_complete_failed:
	cam_soc_util_disable_platform_resource(soc_info, 1, 1);

platform_enable_failed:
	cci_dev->ref_count--;
	cam_cpas_stop(cci_dev->cpas_handle);
@@ -201,9 +210,10 @@ static void cam_cci_init_cci_params(struct cci_device *new_cci_dev)
{
	uint8_t i = 0, j = 0;

	for (i = 0; i < NUM_MASTERS; i++) {
	for (i = 0; i < MASTER_MAX; i++) {
		new_cci_dev->cci_master_info[i].status = 0;
		new_cci_dev->cci_master_info[i].is_first_req = true;
		new_cci_dev->cci_master_info[i].is_initilized = false;
		mutex_init(&new_cci_dev->cci_master_info[i].mutex);
		sema_init(&new_cci_dev->cci_master_info[i].master_sem, 1);
		spin_lock_init(&new_cci_dev->cci_master_info[i].freq_cnt);
@@ -382,27 +392,38 @@ int cam_cci_parse_dt_info(struct platform_device *pdev,
	return 0;
}

int cam_cci_soc_release(struct cci_device *cci_dev)
int cam_cci_soc_release(struct cci_device *cci_dev,
	enum cci_i2c_master_t master)
{
	uint8_t i = 0, rc = 0;
	struct cam_hw_soc_info *soc_info =
		&cci_dev->soc_info;
	struct cam_hw_soc_info *soc_info = &cci_dev->soc_info;

	if (!cci_dev->ref_count || cci_dev->cci_state != CCI_STATE_ENABLED) {
		CAM_ERR(CAM_CCI, "invalid ref count %d / cci state %d",
			cci_dev->ref_count, cci_dev->cci_state);
	if (!cci_dev->ref_count || cci_dev->cci_state != CCI_STATE_ENABLED ||
			!cci_dev->master_active_slave[master]) {
		CAM_ERR(CAM_CCI,
			"invalid cci_dev_ref count %u | cci state %d | master_ref_count %u",
			cci_dev->ref_count, cci_dev->cci_state,
			cci_dev->master_active_slave[master]);
		return -EINVAL;
	}

	if (!(--cci_dev->master_active_slave[master])) {
		cci_dev->cci_master_info[master].is_initilized = false;
		CAM_DBG(CAM_CCI,
			"All submodules are released for master: %d", master);
	}

	if (--cci_dev->ref_count) {
		CAM_DBG(CAM_CCI, "ref_count Exit %d", cci_dev->ref_count);
		CAM_DBG(CAM_CCI, "Submodule release: Ref_count: %d",
			cci_dev->ref_count);
		return 0;
	}
	for (i = 0; i < MASTER_MAX; i++)

	for (i = 0; i < MASTER_MAX; i++) {
		if (cci_dev->write_wq[i])
			flush_workqueue(cci_dev->write_wq[i]);

	for (i = 0; i < MASTER_MAX; i++)
		cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES;
	}

	rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
	if (rc) {
+3 −2
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
 * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
 */

#ifndef _CAM_CCI_SOC_H_
@@ -23,7 +23,8 @@ int cam_cci_init(struct v4l2_subdev *sd,
 *
 * This API releases the CCI and its SOC resources
 */
int cam_cci_soc_release(struct cci_device *cci_dev);
int cam_cci_soc_release(struct cci_device *cci_dev,
	enum cci_i2c_master_t master);

/**
 * @pdev: Platform device