diff options
Diffstat (limited to 'camera/pproc/cpp/msm_cpp_soc.c')
| -rw-r--r-- | camera/pproc/cpp/msm_cpp_soc.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/camera/pproc/cpp/msm_cpp_soc.c b/camera/pproc/cpp/msm_cpp_soc.c new file mode 100644 index 00000000..b481cb75 --- /dev/null +++ b/camera/pproc/cpp/msm_cpp_soc.c @@ -0,0 +1,249 @@ +/* Copyright (c) 2016, 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 <lenovo_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; +} + +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; +} + +static int cpp_get_clk_freq_tbl(struct clk *clk, struct cpp_hw_info *hw_info, + uint32_t min_clk_rate) +{ + uint32_t i; + uint32_t idx = 0; + signed long freq_tbl_entry = 0; + + if ((clk == NULL) || (hw_info == NULL) || (clk->ops == NULL) || + (clk->ops->list_rate == NULL)) { + pr_err("Bad parameter\n"); + return -EINVAL; + } + + for (i = 0; i < MAX_FREQ_TBL; i++) { + freq_tbl_entry = clk->ops->list_rate(clk, i); + pr_debug("entry=%ld\n", freq_tbl_entry); + if (freq_tbl_entry >= 0) { + if (freq_tbl_entry >= min_clk_rate) { + hw_info->freq_tbl[idx++] = freq_tbl_entry; + pr_debug("tbl[%d]=%ld\n", idx-1, + freq_tbl_entry); + } + } else { + pr_debug("freq table returned invalid entry/end %ld\n", + freq_tbl_entry); + break; + } + } + + pr_debug("%s: idx %d", __func__, idx); + hw_info->freq_tbl_count = idx; + + return 0; +} + +int msm_cpp_set_micro_clk(struct cpp_device *cpp_dev) +{ + uint32_t msm_micro_iface_idx; + int rc; + + msm_micro_iface_idx = msm_cpp_get_clock_index(cpp_dev, + "micro_iface_clk"); + if (msm_micro_iface_idx < 0) { + pr_err("Fail to get clock index\n"); + return -EINVAL; + } + + rc = clk_reset(cpp_dev->cpp_clk[msm_micro_iface_idx], + CLK_RESET_ASSERT); + if (rc) { + pr_err("%s:micro_iface_clk 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 = clk_reset(cpp_dev->cpp_clk[msm_micro_iface_idx], + CLK_RESET_DEASSERT); + if (rc) { + pr_err("%s:micro_iface_clk 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) +{ + uint32_t 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(cpp_dev->cpp_clk[msm_cpp_core_clk_idx], + &cpp_dev->hw_info, cpp_dev->min_clk_rate); + 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; +} |
