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

Commit 33ac3e53 authored by Lakshmi Narayana Kalavala's avatar Lakshmi Narayana Kalavala
Browse files

msm: camera: vote for AHB clocks for camera clients



AHB clock is needed to access the hardware registers.
Consolidate all the votes from clients and vote for
the maximum supported ahb clocks.

Change-Id: Iac5ff506b3d272eb183c13ab30203207a7dfa364
Signed-off-by: default avatarLakshmi Narayana Kalavala <lkalaval@codeaurora.org>
Signed-off-by: default avatarRajesh Bondugula <rajeshb@codeaurora.org>
parent 8d378c5f
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -8,10 +8,10 @@ ccflags-y += -Idrivers/media/platform/msm/camera_v2/camera
ccflags-y += -Idrivers/media/platform/msm/camera_v2/jpeg_10
ccflags-y += -Idrivers/media/platform/msm/camera_v2/jpeg_dma
ccflags-y += -Idrivers/media/platform/msm/camera_v2/fd
ccflags-y += -Idrivers/media/platform/msm/camera_v2/smmu
ccflags-y += -Idrivers/media/platform/msm/camera_v2/common

obj-$(CONFIG_MSMB_CAMERA) += common/
obj-$(CONFIG_MSMB_CAMERA) += msm.o
obj-$(CONFIG_MSMB_CAMERA) += smmu/
obj-$(CONFIG_MSMB_CAMERA) += camera/
obj-$(CONFIG_MSMB_CAMERA) += msm_vb2/
obj-$(CONFIG_MSMB_CAMERA) += sensor/
+2 −0
Original line number Diff line number Diff line
ccflags-y += -Idrivers/media/platform/msm/camera_v2/
obj-$(CONFIG_MSMB_CAMERA) += cam_smmu_api.o
obj-$(CONFIG_MSMB_CAMERA) += cam_smmu_api.o cam_hw_ops.o
+273 −0
Original line number Diff line number Diff line
/* Copyright (c) 2015 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.
 */

#define pr_fmt(fmt) "CAM-AHB %s:%d " fmt, __func__, __LINE__
#define TRUE   1
#include <linux/module.h>
#include <linux/msm-bus.h>
#include <linux/msm-bus-board.h>
#include <linux/of_platform.h>
#include "cam_hw_ops.h"

struct cam_ahb_client {
	enum cam_ahb_clk_vote vote;
};

struct cam_bus_vector {
	const char *name;
};

struct cam_ahb_client_data {
	struct msm_bus_scale_pdata *pbus_data;
	u32 ahb_client;
	u32 ahb_clk_state;
	struct msm_bus_vectors *paths;
	struct msm_bus_paths *usecases;
	struct cam_bus_vector *vectors;
	u32 *votes;
	u32 cnt;
	u32 probe_done;
	struct cam_ahb_client clients[CAM_AHB_CLIENT_MAX];
	struct mutex lock;
};

/* Note: The mask array defined here should match
 * the order of strings and number of strings
 * in dtsi bus-vectors
 */

static enum cam_ahb_clk_vote mask[] = {
	CAMERA_AHB_SUSPEND_VOTE,
	CAMERA_AHB_SVS_VOTE,
	CAMERA_AHB_NOMINAL_VOTE,
	CAMERA_AHB_TURBO_VOTE
};

static struct cam_ahb_client_data data;

int get_vector_index(char *name)
{
	int i = 0, rc = -1;

	for (i = 0; i < data.cnt; i++) {
		if (strcmp(name, data.vectors[i].name) == 0)
			return i;
	}

	return rc;
}

int cam_ahb_clk_init(struct platform_device *pdev)
{
	int i = 0, cnt = 0, rc = 0, index = 0;
	struct device_node *of_node;

	if (!pdev) {
		pr_err("invalid pdev argument\n");
		return -EINVAL;
	}

	of_node = pdev->dev.of_node;
	data.cnt = of_property_count_strings(of_node, "bus-vectors");
	if (data.cnt == 0) {
		pr_err("no vectors strings found in device tree, count=%d",
			data.cnt);
		return 0;
	}

	cnt = of_property_count_u32_elems(of_node, "qcom,bus-votes");
	if (cnt == 0) {
		pr_err("no vector values found in device tree, count=%d", cnt);
		return 0;
	}

	if (data.cnt != cnt) {
		pr_err("vector mismatch num of strings=%u, num of values %d\n",
			data.cnt, cnt);
		return -EINVAL;
	}

	pr_debug("number of bus vectors: %d\n", data.cnt);

	data.vectors = devm_kzalloc(&pdev->dev,
		sizeof(struct cam_bus_vector) * cnt,
		GFP_KERNEL);
	if (!data.vectors)
		return -ENOMEM;

	for (i = 0; i < data.cnt; i++) {
		rc = of_property_read_string_index(of_node, "bus-vectors",
				i, &(data.vectors[i].name));
		pr_debug("dbg: names[%d] = %s\n", i, data.vectors[i].name);
		if (rc < 0) {
			pr_err("failed\n");
			rc = -EINVAL;
			goto err1;
		}
	}

	data.paths = devm_kzalloc(&pdev->dev,
		sizeof(struct msm_bus_vectors) * cnt,
		GFP_KERNEL);
	if (!data.paths) {
		rc = -ENOMEM;
		goto err1;
	}

	data.usecases = devm_kzalloc(&pdev->dev,
		sizeof(struct msm_bus_paths) * cnt,
		GFP_KERNEL);
	if (!data.usecases) {
		rc = -ENOMEM;
		goto err2;
	}

	data.pbus_data = devm_kzalloc(&pdev->dev,
		sizeof(struct msm_bus_scale_pdata),
		GFP_KERNEL);
	if (!data.pbus_data) {
		rc = -ENOMEM;
		goto err3;
	}

	data.votes = devm_kzalloc(&pdev->dev, sizeof(u32) * cnt,
		GFP_KERNEL);
	if (!data.votes) {
		rc = -ENOMEM;
		goto err4;
	}

	rc = of_property_read_u32_array(of_node, "qcom,bus-votes",
		data.votes, cnt);

	for (i = 0; i < data.cnt; i++) {
		data.paths[i] = (struct msm_bus_vectors) {
			MSM_BUS_MASTER_AMPSS_M0,
			MSM_BUS_SLAVE_CAMERA_CFG,
			0,
			data.votes[i]
		};
		data.usecases[i] = (struct msm_bus_paths) {
			.num_paths = 1,
			.vectors   = &data.paths[i],
		};
		pr_debug("dbg: votes[%d] = %u\n", i, data.votes[i]);
	}

	*data.pbus_data = (struct msm_bus_scale_pdata) {
		.name = "msm_camera_ahb",
		.num_usecases = data.cnt,
		.usecase = data.usecases,
	};

	data.ahb_client =
		msm_bus_scale_register_client(data.pbus_data);
	if (!data.ahb_client) {
		pr_err("ahb vote registering failed\n");
		rc = -EINVAL;
		goto err5;
	}

	index = get_vector_index("suspend");
	if (index < 0) {
		pr_err("svs vector not supported\n");
		rc = -EINVAL;
		goto err6;
	}

	/* request for svs in init */
	msm_bus_scale_client_update_request(data.ahb_client,
		index);
	data.ahb_clk_state = CAMERA_AHB_SUSPEND_VOTE;
	data.probe_done = TRUE;
	mutex_init(&data.lock);

	pr_debug("dbg, done registering ahb votes\n");
	pr_debug("dbg, clk state :%u, probe :%d\n",
		data.ahb_clk_state, data.probe_done);
	return rc;

err6:
	msm_bus_scale_unregister_client(data.ahb_client);
err5:
	devm_kfree(&pdev->dev, data.votes);
	data.votes = NULL;
err4:
	devm_kfree(&pdev->dev, data.pbus_data);
	data.pbus_data = NULL;
err3:
	devm_kfree(&pdev->dev, data.usecases);
	data.usecases = NULL;
err2:
	devm_kfree(&pdev->dev, data.paths);
	data.paths = NULL;
err1:
	devm_kfree(&pdev->dev, data.vectors);
	data.vectors = NULL;
	return rc;
}
EXPORT_SYMBOL(cam_ahb_clk_init);

int cam_config_ahb_clk(enum cam_ahb_clk_client id, enum cam_ahb_clk_vote vote)
{
	int i = 0, n = 0;
	u32 final_vote = 0;

	if (data.probe_done != TRUE) {
		pr_err("ahb init is not done yet\n");
		return -EINVAL;
	}

	if (vote > CAMERA_AHB_TURBO_VOTE || id >= CAM_AHB_CLIENT_MAX) {
		pr_err("err: invalid argument\n");
		return -EINVAL;
	}

	pr_debug("dbg: id :%u, vote : %u\n", id, vote);
	data.clients[id].vote = vote;

	mutex_lock(&data.lock);

	if (vote == data.ahb_clk_state) {
		pr_debug("dbg: already at desired vote\n");
		mutex_unlock(&data.lock);
		return 0;
	}

	/* oring all the client votes */
	for (i = 0; i < CAM_AHB_CLIENT_MAX; i++)
		final_vote |= data.clients[i].vote;

	pr_debug("dbg: final vote : %u\n", final_vote);
	/* find the max client vote */
	for (n = data.cnt - 1; n >= 0; n--) {
		if (!(final_vote & mask[n]))
			continue;
		else
			break;
	}

	if (n >= 0) {
		if (mask[n] != data.ahb_clk_state) {
			msm_bus_scale_client_update_request(data.ahb_client, n);
			data.ahb_clk_state = mask[n];
			pr_debug("dbg: state : %u, vote : %d\n",
				data.ahb_clk_state, n);
		}
	} else {
		pr_err("err: no bus vector found\n");
		return -EINVAL;
	}
	mutex_unlock(&data.lock);
	return 0;
}
EXPORT_SYMBOL(cam_config_ahb_clk);
+37 −0
Original line number Diff line number Diff line
/* Copyright (c) 2015 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.
 */

enum cam_ahb_clk_vote {
	/* need to update the voting requests
	 * according to dtsi entries.
	 */
	CAMERA_AHB_SUSPEND_VOTE = 0x01,
	CAMERA_AHB_SVS_VOTE = 0x02,
	CAMERA_AHB_NOMINAL_VOTE = 0x04,
	CAMERA_AHB_TURBO_VOTE = 0x08,
};

enum cam_ahb_clk_client {
	CAM_AHB_CLIENT_CSIPHY,
	CAM_AHB_CLIENT_CSID,
	CAM_AHB_CLIENT_CCI,
	CAM_AHB_CLIENT_ISPIF,
	CAM_AHB_CLIENT_VFE,
	CAM_AHB_CLIENT_CPP,
	CAM_AHB_CLIENT_FD,
	CAM_AHB_CLIENT_JPEG,
	CAM_AHB_CLIENT_MAX
};

int cam_config_ahb_clk(enum cam_ahb_clk_client id,
	enum cam_ahb_clk_vote vote);
int cam_ahb_clk_init(struct platform_device *pdev);
Loading