android_kernel_xiaomi_sm8350/pll/pll_util.c
Narendra Muppalla 3709853456 Display drivers kernel project initial snapshot
This change brings msm display driver including sde,
dp, dsi, rotator, dsi pll and dp pll from base 4.19 kernel
project. It is first source code snapshot from base kernel project.

Change-Id: Iec864c064ce5ea04e170f24414c728684002f284
Signed-off-by: Narendra Muppalla <NarendraM@codeaurora.org>
2019-04-14 22:20:59 -07:00

382 lines
8.9 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013-2019, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/of_address.h>
#include <linux/dma-mapping.h>
#include <linux/vmalloc.h>
#include <linux/memblock.h>
#include "pll_drv.h"
/**
* mdss_pll_get_mp_by_reg_name() -- Find power module by regulator name
*@pll_res: Pointer to the PLL resource
*@name: Regulator name as specified in the pll dtsi
*
* This is a helper function to retrieve the regulator information
* for each pll resource.
*/
struct dss_vreg *mdss_pll_get_mp_by_reg_name(struct mdss_pll_resources *pll_res
, char *name)
{
struct dss_vreg *regulator = NULL;
int i;
if ((pll_res == NULL) || (pll_res->mp.vreg_config == NULL)) {
pr_err("%s Invalid PLL resource\n", __func__);
goto error;
}
regulator = pll_res->mp.vreg_config;
for (i = 0; i < pll_res->mp.num_vreg; i++) {
if (!strcmp(name, regulator->vreg_name)) {
pr_debug("Found regulator match for %s\n", name);
break;
}
regulator++;
}
error:
return regulator;
}
int mdss_pll_util_resource_enable(struct mdss_pll_resources *pll_res,
bool enable)
{
int rc = 0;
struct dss_module_power *mp = &pll_res->mp;
if (enable) {
rc = msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable);
if (rc) {
pr_err("Failed to enable vregs rc=%d\n", rc);
goto vreg_err;
}
rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
if (rc) {
pr_err("Failed to set clock rate rc=%d\n", rc);
goto clk_err;
}
rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
if (rc) {
pr_err("clock enable failed rc:%d\n", rc);
goto clk_err;
}
} else {
msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, enable);
}
return rc;
clk_err:
msm_dss_enable_vreg(mp->vreg_config, mp->num_vreg, 0);
vreg_err:
return rc;
}
static int mdss_pll_util_parse_dt_supply(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int i = 0, rc = 0;
u32 tmp = 0;
struct device_node *of_node = NULL, *supply_root_node = NULL;
struct device_node *supply_node = NULL;
struct dss_module_power *mp = &pll_res->mp;
of_node = pdev->dev.of_node;
mp->num_vreg = 0;
supply_root_node = of_get_child_by_name(of_node,
"qcom,platform-supply-entries");
if (!supply_root_node) {
pr_err("no supply entry present\n");
return rc;
}
for_each_child_of_node(supply_root_node, supply_node) {
mp->num_vreg++;
}
if (mp->num_vreg == 0) {
pr_debug("no vreg\n");
return rc;
}
pr_debug("vreg found. count=%d\n", mp->num_vreg);
mp->vreg_config = devm_kzalloc(&pdev->dev, sizeof(struct dss_vreg) *
mp->num_vreg, GFP_KERNEL);
if (!mp->vreg_config) {
rc = -ENOMEM;
return rc;
}
for_each_child_of_node(supply_root_node, supply_node) {
const char *st = NULL;
rc = of_property_read_string(supply_node,
"qcom,supply-name", &st);
if (rc) {
pr_err(":error reading name. rc=%d\n", rc);
goto error;
}
strlcpy(mp->vreg_config[i].vreg_name, st,
sizeof(mp->vreg_config[i].vreg_name));
rc = of_property_read_u32(supply_node,
"qcom,supply-min-voltage", &tmp);
if (rc) {
pr_err(": error reading min volt. rc=%d\n", rc);
goto error;
}
mp->vreg_config[i].min_voltage = tmp;
rc = of_property_read_u32(supply_node,
"qcom,supply-max-voltage", &tmp);
if (rc) {
pr_err(": error reading max volt. rc=%d\n", rc);
goto error;
}
mp->vreg_config[i].max_voltage = tmp;
rc = of_property_read_u32(supply_node,
"qcom,supply-enable-load", &tmp);
if (rc) {
pr_err(": error reading enable load. rc=%d\n", rc);
goto error;
}
mp->vreg_config[i].enable_load = tmp;
rc = of_property_read_u32(supply_node,
"qcom,supply-disable-load", &tmp);
if (rc) {
pr_err(": error reading disable load. rc=%d\n", rc);
goto error;
}
mp->vreg_config[i].disable_load = tmp;
rc = of_property_read_u32(supply_node,
"qcom,supply-pre-on-sleep", &tmp);
if (rc)
pr_debug("error reading supply pre sleep value. rc=%d\n",
rc);
mp->vreg_config[i].pre_on_sleep = (!rc ? tmp : 0);
rc = of_property_read_u32(supply_node,
"qcom,supply-pre-off-sleep", &tmp);
if (rc)
pr_debug("error reading supply pre sleep value. rc=%d\n",
rc);
mp->vreg_config[i].pre_off_sleep = (!rc ? tmp : 0);
rc = of_property_read_u32(supply_node,
"qcom,supply-post-on-sleep", &tmp);
if (rc)
pr_debug("error reading supply post sleep value. rc=%d\n",
rc);
mp->vreg_config[i].post_on_sleep = (!rc ? tmp : 0);
rc = of_property_read_u32(supply_node,
"qcom,supply-post-off-sleep", &tmp);
if (rc)
pr_debug("error reading supply post sleep value. rc=%d\n",
rc);
mp->vreg_config[i].post_off_sleep = (!rc ? tmp : 0);
pr_debug("%s min=%d, max=%d, enable=%d, disable=%d, preonsleep=%d, postonsleep=%d, preoffsleep=%d, postoffsleep=%d\n",
mp->vreg_config[i].vreg_name,
mp->vreg_config[i].min_voltage,
mp->vreg_config[i].max_voltage,
mp->vreg_config[i].enable_load,
mp->vreg_config[i].disable_load,
mp->vreg_config[i].pre_on_sleep,
mp->vreg_config[i].post_on_sleep,
mp->vreg_config[i].pre_off_sleep,
mp->vreg_config[i].post_off_sleep);
++i;
rc = 0;
}
return rc;
error:
if (mp->vreg_config) {
devm_kfree(&pdev->dev, mp->vreg_config);
mp->vreg_config = NULL;
mp->num_vreg = 0;
}
return rc;
}
static int mdss_pll_util_parse_dt_clock(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
u32 i = 0, rc = 0;
struct dss_module_power *mp = &pll_res->mp;
const char *clock_name;
u32 clock_rate;
mp->num_clk = of_property_count_strings(pdev->dev.of_node,
"clock-names");
if (mp->num_clk <= 0) {
pr_err("clocks are not defined\n");
goto clk_err;
}
mp->clk_config = devm_kzalloc(&pdev->dev,
sizeof(struct dss_clk) * mp->num_clk, GFP_KERNEL);
if (!mp->clk_config) {
rc = -ENOMEM;
mp->num_clk = 0;
goto clk_err;
}
for (i = 0; i < mp->num_clk; i++) {
of_property_read_string_index(pdev->dev.of_node, "clock-names",
i, &clock_name);
strlcpy(mp->clk_config[i].clk_name, clock_name,
sizeof(mp->clk_config[i].clk_name));
of_property_read_u32_index(pdev->dev.of_node, "clock-rate",
i, &clock_rate);
mp->clk_config[i].rate = clock_rate;
if (!clock_rate)
mp->clk_config[i].type = DSS_CLK_AHB;
else
mp->clk_config[i].type = DSS_CLK_PCLK;
}
clk_err:
return rc;
}
static void mdss_pll_free_bootmem(u32 mem_addr, u32 size)
{
unsigned long pfn_start, pfn_end, pfn_idx;
pfn_start = mem_addr >> PAGE_SHIFT;
pfn_end = (mem_addr + size) >> PAGE_SHIFT;
for (pfn_idx = pfn_start; pfn_idx < pfn_end; pfn_idx++)
free_reserved_page(pfn_to_page(pfn_idx));
}
static int mdss_pll_util_parse_dt_dfps(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0;
struct device_node *pnode;
const u32 *addr;
struct vm_struct *area;
u64 size;
u32 offsets[2];
unsigned long virt_add;
pnode = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
if (IS_ERR_OR_NULL(pnode)) {
rc = PTR_ERR(pnode);
goto pnode_err;
}
addr = of_get_address(pnode, 0, &size, NULL);
if (!addr) {
pr_err("failed to parse the dfps memory address\n");
rc = -EINVAL;
goto pnode_err;
}
/* maintain compatibility for 32/64 bit */
offsets[0] = (u32) of_read_ulong(addr, 2);
offsets[1] = (u32) size;
area = get_vm_area(offsets[1], VM_IOREMAP);
if (!area) {
rc = -ENOMEM;
goto dfps_mem_err;
}
virt_add = (unsigned long)area->addr;
rc = ioremap_page_range(virt_add, (virt_add + offsets[1]),
offsets[0], PAGE_KERNEL);
if (rc) {
rc = -ENOMEM;
goto ioremap_err;
}
pll_res->dfps = kzalloc(sizeof(struct dfps_info), GFP_KERNEL);
if (IS_ERR_OR_NULL(pll_res->dfps)) {
rc = PTR_ERR(pll_res->dfps);
pr_err("couldn't allocate dfps kernel memory\n");
goto addr_err;
}
/* memcopy complete dfps structure from kernel virtual memory */
memcpy_fromio(pll_res->dfps, area->addr, sizeof(struct dfps_info));
addr_err:
if (virt_add)
unmap_kernel_range(virt_add, (unsigned long) size);
ioremap_err:
if (area)
vfree(area->addr);
dfps_mem_err:
/* free the dfps memory here */
memblock_free(offsets[0], offsets[1]);
mdss_pll_free_bootmem(offsets[0], offsets[1]);
pnode_err:
if (pnode)
of_node_put(pnode);
dma_release_declared_memory(&pdev->dev);
return rc;
}
int mdss_pll_util_resource_parse(struct platform_device *pdev,
struct mdss_pll_resources *pll_res)
{
int rc = 0;
struct dss_module_power *mp = &pll_res->mp;
rc = mdss_pll_util_parse_dt_supply(pdev, pll_res);
if (rc) {
pr_err("vreg parsing failed rc=%d\n", rc);
goto end;
}
rc = mdss_pll_util_parse_dt_clock(pdev, pll_res);
if (rc) {
pr_err("clock name parsing failed rc=%d\n", rc);
goto clk_err;
}
if (mdss_pll_util_parse_dt_dfps(pdev, pll_res))
pr_err("dfps not enabled!\n");
return rc;
clk_err:
devm_kfree(&pdev->dev, mp->vreg_config);
mp->num_vreg = 0;
end:
return rc;
}