blob: d733d2a8993ddffe05bf0ce377d61c379ed9ce25 [file] [log] [blame]
/* Copyright (c) 2016, 2018, 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) "MSM-CPP-SOC %s:%d " fmt, __func__, __LINE__
#include <linux/clk/msm-clk.h>
#include <linux/clk/msm-clk-provider.h>
#include <linux/delay.h>
#include <media/msmb_pproc.h>
#include "msm_cpp.h"
#define CPP_DT_READ_U32_ERR(_dev, _key, _str, _ret, _out) { \
_key = _str; \
_ret = of_property_read_u32(_dev, _key, &_out); \
if (_ret) \
break; \
}
#define CPP_DT_READ_U32(_dev, _str, _out) { \
of_property_read_u32(_dev, _str, &_out); \
}
void msm_cpp_fetch_dt_params(struct cpp_device *cpp_dev)
{
int rc = 0;
struct device_node *of_node = cpp_dev->pdev->dev.of_node;
if (!of_node) {
pr_err("%s: invalid params\n", __func__);
return;
}
of_property_read_u32(of_node, "cell-index", &cpp_dev->pdev->id);
rc = of_property_read_u32(of_node, "qcom,min-clock-rate",
&cpp_dev->min_clk_rate);
if (rc < 0) {
pr_debug("min-clk-rate not defined, setting it to 0\n");
cpp_dev->min_clk_rate = 0;
}
rc = of_property_read_u32(of_node, "qcom,bus-master",
&cpp_dev->bus_master_flag);
if (rc)
cpp_dev->bus_master_flag = 0;
if (of_property_read_bool(of_node, "qcom,micro-reset"))
cpp_dev->micro_reset = 1;
else
cpp_dev->micro_reset = 0;
}
int msm_cpp_get_clock_index(struct cpp_device *cpp_dev, const char *clk_name)
{
uint32_t i = 0;
for (i = 0; i < cpp_dev->num_clks; i++) {
if (!strcmp(clk_name, cpp_dev->clk_info[i].clk_name))
return i;
}
return -EINVAL;
}
int msm_cpp_get_regulator_index(struct cpp_device *cpp_dev,
const char *regulator_name)
{
uint32_t i = 0;
for (i = 0; i < cpp_dev->num_reg; i++) {
if (!strcmp(regulator_name, cpp_dev->cpp_vdd[i].name))
return i;
}
return -EINVAL;
}
static int cpp_get_clk_freq_tbl_dt(struct cpp_device *cpp_dev)
{
uint32_t i, count, min_clk_rate;
uint32_t idx = 0;
struct device_node *of_node;
uint32_t *rates;
int32_t rc = 0;
struct cpp_hw_info *hw_info;
if (cpp_dev == NULL) {
pr_err("Bad parameter\n");
rc = -EINVAL;
goto err;
}
of_node = cpp_dev->pdev->dev.of_node;
min_clk_rate = cpp_dev->min_clk_rate;
hw_info = &cpp_dev->hw_info;
if ((hw_info == NULL) || (of_node == NULL)) {
pr_err("Invalid hw_info %pK or ofnode %pK\n", hw_info, of_node);
rc = -EINVAL;
goto err;
}
count = of_property_count_u32_elems(of_node, "qcom,src-clock-rates");
if ((count == 0) || (count > MAX_FREQ_TBL)) {
pr_err("Clock count is invalid\n");
rc = -EINVAL;
goto err;
}
rates = devm_kcalloc(&cpp_dev->pdev->dev, count, sizeof(uint32_t),
GFP_KERNEL);
if (!rates) {
rc = -ENOMEM;
goto err;
}
rc = of_property_read_u32_array(of_node, "qcom,src-clock-rates",
rates, count);
if (rc) {
rc = -EINVAL;
goto mem_free;
}
for (i = 0; i < count; i++) {
pr_debug("entry=%d\n", rates[i]);
if (rates[i] >= min_clk_rate) {
hw_info->freq_tbl[idx++] = rates[i];
pr_debug("tbl[%d]=%d\n", idx-1, rates[i]);
}
}
pr_debug("%s: idx %d\n", __func__, idx);
hw_info->freq_tbl_count = idx;
mem_free:
devm_kfree(&cpp_dev->pdev->dev, rates);
err:
return rc;
}
int msm_cpp_set_micro_clk(struct cpp_device *cpp_dev)
{
int rc;
rc = reset_control_assert(cpp_dev->micro_iface_reset);
if (rc) {
pr_err("%s:micro_iface_reset assert failed\n",
__func__);
return -EINVAL;
}
/*
* Below usleep values are chosen based on experiments
* and this was the smallest number which works. This
* sleep is needed to leave enough time for Microcontroller
* to resets all its registers.
*/
usleep_range(1000, 1200);
rc = reset_control_deassert(cpp_dev->micro_iface_reset);
if (rc) {
pr_err("%s:micro_iface_reset de-assert failed\n", __func__);
return -EINVAL;
}
/*
* Below usleep values are chosen based on experiments
* and this was the smallest number which works. This
* sleep is needed to leave enough time for Microcontroller
* to resets all its registers.
*/
usleep_range(1000, 1200);
return 0;
}
int msm_update_freq_tbl(struct cpp_device *cpp_dev)
{
int msm_cpp_core_clk_idx;
int rc = 0;
msm_cpp_core_clk_idx = msm_cpp_get_clock_index(cpp_dev, "cpp_core_clk");
if (msm_cpp_core_clk_idx < 0) {
pr_err("%s: fail to get clock index\n", __func__);
rc = msm_cpp_core_clk_idx;
return rc;
}
rc = cpp_get_clk_freq_tbl_dt(cpp_dev);
if (rc < 0) {
pr_err("%s: fail to get frequency table\n", __func__);
return rc;
}
return rc;
}
long msm_cpp_set_core_clk(struct cpp_device *cpp_dev, long rate, int idx)
{
long rc = 0;
rc = msm_camera_clk_set_rate(&cpp_dev->pdev->dev,
cpp_dev->cpp_clk[idx], rate);
if (rc < 0) {
pr_err("%s: fail to get frequency table\n", __func__);
return rc;
}
return rc;
}
int msm_cpp_read_payload_params_from_dt(struct cpp_device *cpp_dev)
{
struct platform_device *pdev = cpp_dev->pdev;
struct device_node *fw_info_node = NULL, *dev_node = NULL;
char *key = "qcom,cpp-fw-payload-info";
struct msm_cpp_payload_params *payload_params;
int ret = 0;
if (!pdev || !pdev->dev.of_node) {
pr_err("%s: Invalid platform device/node\n", __func__);
ret = -ENODEV;
goto no_cpp_node;
}
dev_node = pdev->dev.of_node;
fw_info_node = of_find_node_by_name(dev_node, key);
if (!fw_info_node) {
ret = -ENODEV;
goto no_binding;
}
payload_params = &cpp_dev->payload_params;
memset(payload_params, 0x0, sizeof(struct msm_cpp_payload_params));
do {
CPP_DT_READ_U32_ERR(fw_info_node, key, "qcom,stripe-base", ret,
payload_params->stripe_base);
CPP_DT_READ_U32_ERR(fw_info_node, key, "qcom,plane-base", ret,
payload_params->plane_base);
CPP_DT_READ_U32_ERR(fw_info_node, key, "qcom,stripe-size", ret,
payload_params->stripe_size);
CPP_DT_READ_U32_ERR(fw_info_node, key, "qcom,plane-size", ret,
payload_params->plane_size);
CPP_DT_READ_U32_ERR(fw_info_node, key, "qcom,fe-ptr-off", ret,
payload_params->rd_pntr_off);
CPP_DT_READ_U32_ERR(fw_info_node, key, "qcom,we-ptr-off", ret,
payload_params->wr_0_pntr_off);
CPP_DT_READ_U32(fw_info_node, "qcom,ref-fe-ptr-off",
payload_params->rd_ref_pntr_off);
CPP_DT_READ_U32(fw_info_node, "qcom,ref-we-ptr-off",
payload_params->wr_ref_pntr_off);
CPP_DT_READ_U32(fw_info_node, "qcom,we-meta-ptr-off",
payload_params->wr_0_meta_data_wr_pntr_off);
CPP_DT_READ_U32(fw_info_node, "qcom,fe-mmu-pf-ptr-off",
payload_params->fe_mmu_pf_ptr_off);
CPP_DT_READ_U32(fw_info_node, "qcom,ref-fe-mmu-pf-ptr-off",
payload_params->ref_fe_mmu_pf_ptr_off);
CPP_DT_READ_U32(fw_info_node, "qcom,we-mmu-pf-ptr-off",
payload_params->we_mmu_pf_ptr_off);
CPP_DT_READ_U32(fw_info_node, "qcom,dup-we-mmu-pf-ptr-off",
payload_params->dup_we_mmu_pf_ptr_off);
CPP_DT_READ_U32(fw_info_node, "qcom,ref-we-mmu-pf-ptr-off",
payload_params->ref_we_mmu_pf_ptr_off);
CPP_DT_READ_U32(fw_info_node, "qcom,set-group-buffer-len",
payload_params->set_group_buffer_len);
CPP_DT_READ_U32(fw_info_node, "qcom,dup-frame-indicator-off",
payload_params->dup_frame_indicator_off);
} while (0);
no_binding:
if (ret)
pr_err("%s: Error reading binding %s, ret %d\n",
__func__, key, ret);
no_cpp_node:
return ret;
}