/* 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 #include #include #include #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; }