[Freedreno] [DPU PATCH 2/3] drm/msm/dp: add displayPort driver support
Jordan Crouse
jcrouse at codeaurora.org
Thu Oct 11 17:43:48 UTC 2018
On Wed, Oct 10, 2018 at 10:15:58AM -0700, Chandan Uddaraju wrote:
> Add the needed displayPort files to enable DP driver
> on msm target.
>
> "dp_display" module is the main module that calls into
> other sub-modules. "dp_drm" file represents the interface
> between DRM framework and DP driver.
>
> Signed-off-by: Chandan Uddaraju <chandanu at codeaurora.org>
> ---
> drivers/gpu/drm/msm/Kconfig | 9 +
> drivers/gpu/drm/msm/Makefile | 15 +-
> drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.c | 206 ++++
> drivers/gpu/drm/msm/disp/dpu1/dpu_io_util.h | 44 +
> drivers/gpu/drm/msm/dp/dp_aux.c | 570 ++++++++++
> drivers/gpu/drm/msm/dp/dp_aux.h | 44 +
> drivers/gpu/drm/msm/dp/dp_catalog.c | 1188 ++++++++++++++++++++
> drivers/gpu/drm/msm/dp/dp_catalog.h | 144 +++
> drivers/gpu/drm/msm/dp/dp_ctrl.c | 1475 +++++++++++++++++++++++++
> drivers/gpu/drm/msm/dp/dp_ctrl.h | 50 +
> drivers/gpu/drm/msm/dp/dp_debug.c | 507 +++++++++
> drivers/gpu/drm/msm/dp/dp_debug.h | 81 ++
> drivers/gpu/drm/msm/dp/dp_display.c | 977 +++++++++++++++++
> drivers/gpu/drm/msm/dp/dp_display.h | 55 +
> drivers/gpu/drm/msm/dp/dp_drm.c | 542 ++++++++++
> drivers/gpu/drm/msm/dp/dp_drm.h | 52 +
> drivers/gpu/drm/msm/dp/dp_extcon.c | 400 +++++++
> drivers/gpu/drm/msm/dp/dp_extcon.h | 111 ++
> drivers/gpu/drm/msm/dp/dp_link.c | 1549 +++++++++++++++++++++++++++
> drivers/gpu/drm/msm/dp/dp_link.h | 184 ++++
> drivers/gpu/drm/msm/dp/dp_panel.c | 624 +++++++++++
> drivers/gpu/drm/msm/dp/dp_panel.h | 121 +++
> drivers/gpu/drm/msm/dp/dp_parser.c | 679 ++++++++++++
> drivers/gpu/drm/msm/dp/dp_parser.h | 205 ++++
> drivers/gpu/drm/msm/dp/dp_power.c | 599 +++++++++++
> drivers/gpu/drm/msm/dp/dp_power.h | 57 +
> drivers/gpu/drm/msm/dp/dp_reg.h | 357 ++++++
> drivers/gpu/drm/msm/msm_drv.c | 2 +
> drivers/gpu/drm/msm/msm_drv.h | 22 +
> include/drm/drm_dp_helper.h | 19 +
> 30 files changed, 10887 insertions(+), 1 deletion(-)
This is my last go - the end is in sight.
<snip all the way to dp_parser.c>
> diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c
> new file mode 100644
> index 0000000..a366dc5
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/dp_parser.c
> @@ -0,0 +1,679 @@
> +/*
> + * Copyright (c) 2012-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.
> + *
> + */
SPDX please
> +#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
> +
> +#include <linux/of_gpio.h>
> +
> +#include "dp_parser.h"
> +
> +static void dp_parser_unmap_io_resources(struct dp_parser *parser)
> +{
> + struct dp_io *io = &parser->io;
> +
> + msm_dss_iounmap(&io->dp_ahb);
> + msm_dss_iounmap(&io->dp_aux);
> + msm_dss_iounmap(&io->dp_link);
> + msm_dss_iounmap(&io->dp_p0);
> + msm_dss_iounmap(&io->phy_io);
> + msm_dss_iounmap(&io->ln_tx0_io);
> + msm_dss_iounmap(&io->ln_tx0_io);
> + msm_dss_iounmap(&io->dp_pll_io);
> + msm_dss_iounmap(&io->dp_cc_io);
> + msm_dss_iounmap(&io->usb3_dp_com);
> + msm_dss_iounmap(&io->qfprom_io);
> +}
If you use devm_ to ioremap as suggested previously, then this all ends up going
away.
> +static int dp_parser_ctrl_res(struct dp_parser *parser)
> +{
> + int rc = 0;
> + u32 index;
> + struct platform_device *pdev = parser->pdev;
> + struct device_node *of_node = parser->pdev->dev.of_node;
> + struct dp_io *io = &parser->io;
> +
> + rc = of_property_read_u32(of_node, "cell-index", &index);
> + if (rc) {
> + pr_err("cell-index not specified, rc=%d\n", rc);
> + goto err;
> + }
This value doesn't appear to be used anywhere in this code, so why are you
grabbing it?
> + rc = msm_dss_ioremap_byname(pdev, &io->dp_ahb, "dp_ahb");
> + if (rc) {
> + pr_err("unable to remap dp io resources\n");
msm_ioremap() helpfully gives an informative error message including the name of
the region that failed so you don't need a redundant message here.
> + goto err;
> + }
> +
> + rc = msm_dss_ioremap_byname(pdev, &io->dp_aux, "dp_aux");
> + if (rc) {
> + pr_err("unable to remap dp io resources\n");
> + goto err;
> + }
> +
> + rc = msm_dss_ioremap_byname(pdev, &io->dp_link, "dp_link");
> + if (rc) {
> + pr_err("unable to remap dp io resources\n");
> + goto err;
> + }
> +
> + rc = msm_dss_ioremap_byname(pdev, &io->dp_p0, "dp_p0");
> + if (rc) {
> + pr_err("unable to remap dp io resources\n");
> + goto err;
> + }
> +
> + rc = msm_dss_ioremap_byname(pdev, &io->phy_io, "dp_phy");
> + if (rc) {
> + pr_err("unable to remap dp PHY resources\n");
> + goto err;
> + }
> +
> + rc = msm_dss_ioremap_byname(pdev, &io->ln_tx0_io, "dp_ln_tx0");
> + if (rc) {
> + pr_err("unable to remap dp TX0 resources\n");
> + goto err;
> + }
> +
> + rc = msm_dss_ioremap_byname(pdev, &io->ln_tx1_io, "dp_ln_tx1");
> + if (rc) {
> + pr_err("unable to remap dp TX1 resources\n");
> + goto err;
> + }
> +
> + rc = msm_dss_ioremap_byname(pdev, &io->dp_pll_io, "dp_pll");
> + if (rc) {
> + pr_err("unable to remap DP PLL resources\n");
> + goto err;
> + }
> +
> + rc = msm_dss_ioremap_byname(pdev, &io->usb3_dp_com, "usb3_dp_com");
> + if (rc) {
> + pr_err("unable to remap USB3 DP com resources\n");
> + goto err;
> + }
> +
> + if (msm_dss_ioremap_byname(pdev, &io->dp_cc_io, "dp_mmss_cc")) {
> + pr_err("unable to remap dp MMSS_CC resources\n");
> + goto err;
> + }
> +
> + if (msm_dss_ioremap_byname(pdev, &io->qfprom_io, "qfprom_physical"))
> + pr_warn("unable to remap dp qfprom resources\n");
> +
It doesn't look like you are using this in the current code so you can remove it
but if you did need to read fuses use nvram_cell instead.
> + return 0;
> +err:
> + dp_parser_unmap_io_resources(parser);
> + return rc;
> +}
This whole function is crying out for an array:
struct {
void __iomem **base;
const char *name;
} regions[] = {
{ &io->dp_aux.base, "dp_aux" },
...
{ &io->dp_cc_io.base, "dp_mms_cc" },
{ NULL, NULL }
};
for(i = 0; regions[i].name; i++) {
void __iomem *ptr = msm_ioremap(pdev, regions[i].name, NULL);
if (IS_ERR(ptr))
return -ENODEV;
*(regions[i].base) = ptr;
}
return 0;
> +
> +static const char *dp_get_phy_aux_config_property(u32 cfg_type)
> +{
> + switch (cfg_type) {
> + case PHY_AUX_CFG0:
> + return "qcom,aux-cfg0-settings";
> + case PHY_AUX_CFG1:
> + return "qcom,aux-cfg1-settings";
> + case PHY_AUX_CFG2:
> + return "qcom,aux-cfg2-settings";
> + case PHY_AUX_CFG3:
> + return "qcom,aux-cfg3-settings";
> + case PHY_AUX_CFG4:
> + return "qcom,aux-cfg4-settings";
> + case PHY_AUX_CFG5:
> + return "qcom,aux-cfg5-settings";
> + case PHY_AUX_CFG6:
> + return "qcom,aux-cfg6-settings";
> + case PHY_AUX_CFG7:
> + return "qcom,aux-cfg7-settings";
> + case PHY_AUX_CFG8:
> + return "qcom,aux-cfg8-settings";
> + case PHY_AUX_CFG9:
> + return "qcom,aux-cfg9-settings";
> + default:
> + return "unknown";
> + }
> +}
Since CFG0 is 0 and CFG1 is and so on instead of calling
a function you can just construct the string inline when you need it....
> +static void dp_parser_phy_aux_cfg_reset(struct dp_parser *parser)
> +{
> + int i = 0;
> +
> + for (i = 0; i < PHY_AUX_CFG_MAX; i++)
> + parser->aux_cfg[i] = (const struct dp_aux_cfg){ 0 };
> +}
> +
> +static int dp_parser_aux(struct dp_parser *parser)
> +{
> + struct device_node *of_node = parser->pdev->dev.of_node;
> + int len = 0, i = 0, j = 0, config_count = 0;
> + const char *data;
> + int const minimum_config_count = 1;
> +
> + for (i = 0; i < PHY_AUX_CFG_MAX; i++) {
> + const char *property = dp_get_phy_aux_config_property(i);
Here, instead of this, do
char property[24];
snprintf(property, sizeof(property), "qcom,aux-cfg%d-settings", i);
> + data = of_get_property(of_node, property, &len);
> + if (!data) {
> + pr_err("Unable to read %s\n", property);
> + goto error;
> + }
> +
> + config_count = len - 1;
> + if ((config_count < minimum_config_count) ||
> + (config_count > DP_AUX_CFG_MAX_VALUE_CNT)) {
> + pr_err("Invalid config count (%d) configs for %s\n",
> + config_count, property);
> + goto error;
> + }
> +
> + parser->aux_cfg[i].offset = data[0];
> + parser->aux_cfg[i].cfg_cnt = config_count;
> + pr_debug("%s offset=0x%x, cfg_cnt=%d\n",
> + property,
> + parser->aux_cfg[i].offset,
> + parser->aux_cfg[i].cfg_cnt);
> + for (j = 1; j < len; j++) {
> + parser->aux_cfg[i].lut[j - 1] = data[j];
> + pr_debug("%s lut[%d]=0x%x\n",
> + property,
> + i,
> + parser->aux_cfg[i].lut[j - 1]);
> + }
qcom,aux-cfgN-settings appears to be an array. Seems like it would be easier
just to use of_property_read_u32_array()? Seems like it would save yourself a
bit of work.
> + }
> + return 0;
> +
> +error:
> + dp_parser_phy_aux_cfg_reset(parser);
> + return -EINVAL;
> +}
> +
> +static int dp_parser_misc(struct dp_parser *parser)
> +{
> + int rc = 0;
> + struct device_node *of_node = parser->pdev->dev.of_node;
> +
> + rc = of_property_read_u32(of_node,
> + "qcom,max-pclk-frequency-khz", &parser->max_pclk_khz);
> + if (rc)
> + parser->max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ;
On error, of_property_read_u32 and friends will not touch the passed in pointer,
so you can safely set the pointer to a default and then not worry about checking
the value of the function - so this can just turn into:
parser->max_pclk_hz = DP_MAX_PIXEL_CLK_KHZ;
of_property_read_u32(of_node, "qcom,max-pclk-frequency-khz",
&parser->max_pclk_khz);
> +
> + return 0;
This function doesn't need to return anything. And technically, it doesn't need
to be a function, but the place where it gets called is entirely comprised of
functions so thats probably not a huge deal.
> +}
> +
> +static int dp_parser_pinctrl(struct dp_parser *parser)
> +{
> + int rc = 0;
> + struct dp_pinctrl *pinctrl = &parser->pinctrl;
> +
> + pinctrl->pin = devm_pinctrl_get(&parser->pdev->dev);
> +
> + if (IS_ERR_OR_NULL(pinctrl->pin)) {
It looks to me like pinctrl_get only returns ERR_PTR so you can just use
IS_ERR() for your check.
> + rc = PTR_ERR(pinctrl->pin);
> + pr_err("failed to get pinctrl, rc=%d\n", rc);
And you can just return PTR_ERR(pinctrl->pin) here
> + goto error;
> + }
> +
> + pinctrl->state_active = pinctrl_lookup_state(pinctrl->pin,
> + "mdss_dp_active");
> + if (IS_ERR_OR_NULL(pinctrl->state_active)) {
Same.
> + rc = PTR_ERR(pinctrl->state_active);
> + pr_err("failed to get pinctrl active state, rc=%d\n", rc);
> + goto error;
> + }
> +
> + pinctrl->state_suspend = pinctrl_lookup_state(pinctrl->pin,
> + "mdss_dp_sleep");
> + if (IS_ERR_OR_NULL(pinctrl->state_suspend)) {
Same
> + rc = PTR_ERR(pinctrl->state_suspend);
> + pr_err("failed to get pinctrl suspend state, rc=%d\n", rc);
> + goto error;
> + }
> +error:
> + return rc;
You don't need this label if you return as soon as you get the error.
> +}
> +
> +static int dp_parser_gpio(struct dp_parser *parser)
> +{
> + int i = 0;
> + struct device *dev = &parser->pdev->dev;
> + struct device_node *of_node = dev->of_node;
> + struct dss_module_power *mp = &parser->mp[DP_CORE_PM];
> + static const char * const dp_gpios[] = {
> + "qcom,aux-en-gpio",
> + "qcom,aux-sel-gpio",
> + "qcom,usbplug-cc-gpio",
> + };
> +
> + mp->gpio_config = devm_kzalloc(dev,
> + sizeof(struct dss_gpio) * ARRAY_SIZE(dp_gpios), GFP_KERNEL);
> + if (!mp->gpio_config)
> + return -ENOMEM;
> +
> + mp->num_gpio = ARRAY_SIZE(dp_gpios);
> +
> + for (i = 0; i < ARRAY_SIZE(dp_gpios); i++) {
> + mp->gpio_config[i].gpio = of_get_named_gpio(of_node,
> + dp_gpios[i], 0);
> +
> + if (!gpio_is_valid(mp->gpio_config[i].gpio)) {
> + pr_err("%s gpio not specified\n", dp_gpios[i]);
> + return -EINVAL;
> + }
> +
> + strlcpy(mp->gpio_config[i].gpio_name, dp_gpios[i],
> + sizeof(mp->gpio_config[i].gpio_name));
> +
> + mp->gpio_config[i].value = 0;
> + }
> +
> + return 0;
> +}
> +
> +static const char *dp_parser_supply_node_name(enum dp_pm_type module)
> +{
> + switch (module) {
> + case DP_CORE_PM: return "qcom,core-supply-entries";
> + case DP_CTRL_PM: return "qcom,ctrl-supply-entries";
> + case DP_PHY_PM: return "qcom,phy-supply-entries";
> + default: return "???";
This isn't possible. At the very least return NULL and give you self a chance
to detect the error - otherwise you'll be returning odd error messages with ????
in it that would be harder to debug.
> + }
> +}
> +
> +static int dp_parser_get_vreg(struct dp_parser *parser,
> + enum dp_pm_type module)
> +{
> + int i = 0, rc = 0;
> + u32 tmp = 0;
> + const char *pm_supply_name = NULL;
> + struct device_node *supply_node = NULL;
> + struct device_node *of_node = parser->pdev->dev.of_node;
> + struct device_node *supply_root_node = NULL;
> + struct dss_module_power *mp = &parser->mp[module];
> +
> + mp->num_vreg = 0;
> + pm_supply_name = dp_parser_supply_node_name(module);
> + supply_root_node = of_get_child_by_name(of_node, pm_supply_name);
> + if (!supply_root_node) {
> + pr_err("no supply entry present: %s\n", pm_supply_name);
> + goto novreg;
> + }
> +
> + mp->num_vreg = of_get_available_child_count(supply_root_node);
> +
> + if (mp->num_vreg == 0) {
> + pr_debug("no vreg\n");
> + goto novreg;
> + } else {
> + pr_debug("vreg found. count=%d\n", mp->num_vreg);
> + }
> +
> + mp->vreg_config = devm_kzalloc(&parser->pdev->dev,
> + sizeof(struct dss_vreg) * mp->num_vreg, GFP_KERNEL);
> + if (!mp->vreg_config) {
> + rc = -ENOMEM;
> + goto error;
> + }
> +
> + for_each_child_of_node(supply_root_node, supply_node) {
> + const char *st = NULL;
> + /* vreg-name */
> + rc = of_property_read_string(supply_node,
> + "qcom,supply-name", &st);
> + if (rc) {
> + pr_err("error reading name. rc=%d\n",
> + rc);
> + goto error;
> + }
> + snprintf(mp->vreg_config[i].vreg_name,
> + ARRAY_SIZE((mp->vreg_config[i].vreg_name)), "%s", st);
ARRAY_SIZE works for a string, but we prefer sizeof() in most cases. If you
are just copying a string, consider using strncpy() and friends. It will be
faster than snprintf.
> + /* vreg-min-voltage */
> + rc = of_property_read_u32(supply_node,
> + "qcom,supply-min-voltage", &tmp);
As mentioned above, on error they don't touch the value of the pointer so you
don't need to use a temporary variable.
> + if (rc) {
> + pr_err("error reading min volt. rc=%d\n",
> + rc);
> + goto error;
> + }
> + mp->vreg_config[i].min_voltage = tmp;
> +
> + /* vreg-max-voltage */
> + 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;
> +
> + /* enable-load */
> + 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;
> +
> + /* disable-load */
> + 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;
> +
> + pr_debug("%s min=%d, max=%d, enable=%d, disable=%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
> + );
> + ++i;
> + }
> +
> + return rc;
> +
> +error:
> + if (mp->vreg_config) {
You don't need to check for NULL here.
> + devm_kfree(&parser->pdev->dev, mp->vreg_config);
> + mp->vreg_config = NULL;
> + }
> +novreg:
> + mp->num_vreg = 0;
> +
> + return rc;
> +}
> +
> +static void dp_parser_put_vreg_data(struct device *dev,
> + struct dss_module_power *mp)
> +{
> + if (!mp) {
> + DEV_ERR("invalid input\n");
This error message isn't useful when you are taking down the device - just
quietly skip.
> + return;
> + }
> +
> + if (mp->vreg_config) {
> + devm_kfree(dev, mp->vreg_config);
You don't need to kfree managed memory.
> + mp->vreg_config = NULL;
> + }
> + mp->num_vreg = 0;
> +}
> +
> +static int dp_parser_regulator(struct dp_parser *parser)
> +{
> + int i, rc = 0;
> + struct platform_device *pdev = parser->pdev;
> +
> + /* Parse the regulator information */
> + for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
> + rc = dp_parser_get_vreg(parser, i);
> + if (rc) {
> + pr_err("get_dt_vreg_data failed for %s. rc=%d\n",
> + dp_parser_pm_name(i), rc);
dp_parser_get_vreg() is very loud about errors - you don't need to pile on.
> + i--;
> + for (; i >= DP_CORE_PM; i--)
> + dp_parser_put_vreg_data(&pdev->dev,
> + &parser->mp[i]);
> + break;
> + }
> + }
> +
> + return rc;
> +}
> +
> +static bool dp_parser_check_prefix(const char *clk_prefix, const char *clk_name)
> +{
> + return !!strnstr(clk_name, clk_prefix, strlen(clk_name));
This isn't saving you any lines of code - you can do the string compare
directly in the calling functions.
> +}
> +
> +static void dp_parser_put_clk_data(struct device *dev,
> + struct dss_module_power *mp)
> +{
> + if (!mp) {
> + DEV_ERR("%s: invalid input\n", __func__);
You are taking down the device so you don't care if anything is invalid.
> + return;
> + }
> +
> + if (mp->clk_config) {
> + devm_kfree(dev, mp->clk_config);
You don't need to free managed device memory.
> + mp->clk_config = NULL;
> + }
> +
> + mp->num_clk = 0;
> +}
> +
> +static void dp_parser_put_gpio_data(struct device *dev,
> + struct dss_module_power *mp)
> +{
> + if (!mp) {
> + DEV_ERR("%s: invalid input\n", __func__);
As above.
> + return;
> + }
> +
> + if (mp->gpio_config) {
> + devm_kfree(dev, mp->gpio_config);
Also as above.
> + mp->gpio_config = NULL;
> + }
> +
> + mp->num_gpio = 0;
> +}
> +
> +static int dp_parser_init_clk_data(struct dp_parser *parser)
> +{
> + int num_clk = 0, i = 0, rc = 0;
> + int core_clk_count = 0, ctrl_clk_count = 0;
> + const char *core_clk = "core";
> + const char *ctrl_clk = "ctrl";
> + const char *clk_name;
> + struct device *dev = &parser->pdev->dev;
> + struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
> + struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
> +
> + num_clk = of_property_count_strings(dev->of_node, "clock-names");
> + if (num_clk <= 0) {
> + pr_err("no clocks are defined\n");
> + rc = -EINVAL;
> + goto exit;
> + }
> +
> + for (i = 0; i < num_clk; i++) {
> + of_property_read_string_index(dev->of_node,
> + "clock-names", i, &clk_name);
> +
> + if (dp_parser_check_prefix(core_clk, clk_name))
> + core_clk_count++;
According to your bindings, 'core' or 'ctrl' will be the prefix for the clock,
so why are you using strnstr? strncmp works just as well.
!strncmp("core", clk_name, 4)
> +
> + if (dp_parser_check_prefix(ctrl_clk, clk_name))
if (!strncmp("ctrl", clk_name, 4)
> + ctrl_clk_count++;
> + }
> +
> + /* Initialize the CORE power module */
> + if (core_clk_count <= 0) {
core_clk_count clearly can't be less than zero.
> + pr_err("no core clocks are defined\n");
> + rc = -EINVAL;
> + goto exit;
> + }
> +
> + core_power->num_clk = core_clk_count;
> + core_power->clk_config = devm_kzalloc(dev,
> + sizeof(struct dss_clk) * core_power->num_clk,
> + GFP_KERNEL);
> + if (!core_power->clk_config) {
> + rc = -EINVAL;
the return value should be -ENOMEM here.
> + goto exit;
> + }
> +
> + /* Initialize the CTRL power module */
> + if (ctrl_clk_count <= 0) {
Again, this clearly can't be less than zero.
> + pr_err("no ctrl clocks are defined\n");
> + rc = -EINVAL;
> + goto ctrl_clock_error;
> + }
> +
> + ctrl_power->num_clk = ctrl_clk_count;
> + ctrl_power->clk_config = devm_kzalloc(dev,
> + sizeof(struct dss_clk) * ctrl_power->num_clk,
> + GFP_KERNEL);
> + if (!ctrl_power->clk_config) {
> + ctrl_power->num_clk = 0;
> + rc = -EINVAL;
And -ENOMEM again.
> + goto ctrl_clock_error;
> + }
> +
> + return rc;
> +
> +ctrl_clock_error:
> + dp_parser_put_clk_data(dev, core_power);
> +exit:
> + return rc;
Don't use this label - just return the error code directly instead.
> +}
> +
> +static int dp_parser_clock(struct dp_parser *parser)
> +{
> + int rc = 0, i = 0;
> + int num_clk = 0;
> + int core_clk_index = 0, ctrl_clk_index = 0;
> + int core_clk_count = 0, ctrl_clk_count = 0;
> + const char *clk_name;
> + const char *core_clk = "core";
> + const char *ctrl_clk = "ctrl";
> + struct device *dev = &parser->pdev->dev;
> + struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
> + struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
> +
> + core_power = &parser->mp[DP_CORE_PM];
> + ctrl_power = &parser->mp[DP_CTRL_PM];
> +
> + rc = dp_parser_init_clk_data(parser);
> + if (rc) {
> + pr_err("failed to initialize power data\n");
> + rc = -EINVAL;
> + goto exit;
> + }
> +
> + core_clk_count = core_power->num_clk;
> + ctrl_clk_count = ctrl_power->num_clk;
> +
> + num_clk = core_clk_count + ctrl_clk_count;
> +
According to your bindings, not every clock in your list is a core or a ctrl -
are those clocks ignored?
> + for (i = 0; i < num_clk; i++) {
> + of_property_read_string_index(dev->of_node, "clock-names",
> + i, &clk_name);
This implies that a core_ or ctrl_ clock are first in the list. Your bindings
do not specify an order.
> +
> + if (dp_parser_check_prefix(core_clk, clk_name) &&
Again, you should use strncmp() here
> + core_clk_index < core_clk_count) {
If you already went to the trouble of counting you can be pretty sure that
you won't overrun your array.
> + struct dss_clk *clk =
> + &core_power->clk_config[core_clk_index];
> + strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
> + clk->type = DSS_CLK_AHB;
> + core_clk_index++;
> + } else if (dp_parser_check_prefix(ctrl_clk, clk_name) &&
> + ctrl_clk_index < ctrl_clk_count) {
You aren't going to overrun your array.
> + struct dss_clk *clk =
> + &ctrl_power->clk_config[ctrl_clk_index];
> + strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
> + ctrl_clk_index++;
> +
> + if (!strcmp(clk_name, "ctrl_link_clk") ||
> + !strcmp(clk_name, "ctrl_pixel_clk"))
> + clk->type = DSS_CLK_PCLK;
> + else
> + clk->type = DSS_CLK_AHB;
> + }
> + }
There is a lot going on in these two functions but I feel they can be combined.
The number of clocks in each array shouldn't be a mystery - you can
pre-allocate or statically allocate the arrays. That will save you from the
initial step of walking the list to allocate the memory. Then you can have a
single walk of the list, checking the clock type and handle it accordingly.
> +
> + pr_debug("clock parsing successful\n");
> +
> +exit:
> + return rc;
> +}
> +
> +static int dp_parser_parse(struct dp_parser *parser)
> +{
> + int rc = 0;
> +
> + if (!parser) {
> + pr_err("invalid input\n");
> + rc = -EINVAL;
> + goto err;
> + }
>
parser can never be null here.
> + rc = dp_parser_ctrl_res(parser);
> + if (rc)
> + goto err;
You can just return rc here and throughout without the label.
> +
> + rc = dp_parser_aux(parser);
> + if (rc)
> + goto err;
> +
> + rc = dp_parser_misc(parser);
> + if (rc)
> + goto err;
> +
> + rc = dp_parser_clock(parser);
> + if (rc)
> + goto err;
> +
> + rc = dp_parser_regulator(parser);
> + if (rc)
> + goto err;
> +
> + rc = dp_parser_gpio(parser);
> + if (rc)
> + goto err;
> +
> + rc = dp_parser_pinctrl(parser);
> +err:
> + return rc;
> +}
> +
> +struct dp_parser *dp_parser_get(struct platform_device *pdev)
> +{
> + struct dp_parser *parser;
> +
> + parser = devm_kzalloc(&pdev->dev, sizeof(*parser), GFP_KERNEL);
> + if (!parser)
> + return ERR_PTR(-ENOMEM);
> +
> + parser->parse = dp_parser_parse;
> + parser->pdev = pdev;
> +
> + return parser;
> +}
> +
> +void dp_parser_put(struct dp_parser *parser)
> +{
> + int i = 0;
> + struct dss_module_power *power = NULL;
> +
> + if (!parser) {
> + pr_err("invalid parser module\n");
If parser is null, you don't care. Just quietly exit.
> + return;
> + }
> +
> + power = parser->mp;
> +
> + for (i = 0; i < DP_MAX_PM; i++) {
> + dp_parser_put_clk_data(&parser->pdev->dev, &power[i]);
> + dp_parser_put_vreg_data(&parser->pdev->dev, &power[i]);
> + dp_parser_put_gpio_data(&parser->pdev->dev, &power[i]);
> + }
> +
> + devm_kfree(&parser->pdev->dev, parser);
You don't need to free managed device memory.
> +}
> diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h
> new file mode 100644
> index 0000000..b39ea02
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/dp_parser.h
> @@ -0,0 +1,205 @@
> +/*
> + * Copyright (c) 2012-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.
> + *
> + */
SPDX please
> +#ifndef _DP_PARSER_H_
> +#define _DP_PARSER_H_
> +
> +#include "dpu_io_util.h"
> +
> +#define DP_LABEL "MDSS DP DISPLAY"
> +#define AUX_CFG_LEN 10
> +#define DP_MAX_PIXEL_CLK_KHZ 675000
> +
> +enum dp_pm_type {
> + DP_CORE_PM,
> + DP_CTRL_PM,
> + DP_PHY_PM,
> + DP_MAX_PM
> +};
> +
> +static inline const char *dp_parser_pm_name(enum dp_pm_type module)
> +{
> + switch (module) {
> + case DP_CORE_PM: return "DP_CORE_PM";
> + case DP_CTRL_PM: return "DP_CTRL_PM";
> + case DP_PHY_PM: return "DP_PHY_PM";
> + default: return "???";
You go out of your way to make sure that the default case can't happen here.
also this function is only used once, I don't think it needs to be a static
inline in the header.
> + }
> +}
> +
> +/**
> + * struct dp_display_data - display related device tree data.
> + *
> + * @ctrl_node: referece to controller device
> + * @phy_node: reference to phy device
> + * @is_active: is the controller currently active
> + * @name: name of the display
> + * @display_type: type of the display
> + */
> +struct dp_display_data {
> + struct device_node *ctrl_node;
> + struct device_node *phy_node;
> + bool is_active;
> + const char *name;
> + const char *display_type;
> +};
> +
> +/**
> + * struct dp_ctrl_resource - controller's IO related data
> + *
> + * @dp_ahb: controller's ahb mapped memory address
> + * @dp_aux: controller's aux mapped memory address
> + * @dp_link: controller's link mapped memory address
> + * @dp_p0: controller's p0 mapped memory address
> + * @phy_io: phy's mapped memory address
> + * @ln_tx0_io: USB-DP lane TX0's mapped memory address
> + * @ln_tx1_io: USB-DP lane TX1's mapped memory address
> + * @dp_cc_io: DP cc's mapped memory address
> + * @qfprom_io: qfprom's mapped memory address
> + * @dp_pll_io: DP PLL mapped memory address
> + * @usb3_dp_com: USB3 DP PHY combo mapped memory address
> + */
> +struct dp_io {
> + struct dss_io_data ctrl_io;
> + struct dss_io_data dp_ahb;
> + struct dss_io_data dp_aux;
> + struct dss_io_data dp_link;
> + struct dss_io_data dp_p0;
> + struct dss_io_data phy_io;
> + struct dss_io_data ln_tx0_io;
> + struct dss_io_data ln_tx1_io;
> + struct dss_io_data dp_cc_io;
> + struct dss_io_data qfprom_io;
> + struct dss_io_data dp_pll_io;
> + struct dss_io_data usb3_dp_com;
> +};
> +
> +/**
> + * struct dp_pinctrl - DP's pin control
> + *
> + * @pin: pin-controller's instance
> + * @state_active: active state pin control
> + * @state_hpd_active: hpd active state pin control
> + * @state_suspend: suspend state pin control
> + */
> +struct dp_pinctrl {
> + struct pinctrl *pin;
> + struct pinctrl_state *state_active;
> + struct pinctrl_state *state_hpd_active;
> + struct pinctrl_state *state_suspend;
> +};
> +
> +#define DP_ENUM_STR(x) #x
> +#define DP_AUX_CFG_MAX_VALUE_CNT 3
> +/**
> + * struct dp_aux_cfg - DP's AUX configuration settings
> + *
> + * @cfg_cnt: count of the configurable settings for the AUX register
> + * @current_index: current index of the AUX config lut
> + * @offset: register offset of the AUX config register
> + * @lut: look up table for the AUX config values for this register
> + */
> +struct dp_aux_cfg {
> + u32 cfg_cnt;
> + u32 current_index;
> + u32 offset;
> + u32 lut[DP_AUX_CFG_MAX_VALUE_CNT];
> +};
> +
> +/* PHY AUX config registers */
> +enum dp_phy_aux_config_type {
> + PHY_AUX_CFG0,
> + PHY_AUX_CFG1,
> + PHY_AUX_CFG2,
> + PHY_AUX_CFG3,
> + PHY_AUX_CFG4,
> + PHY_AUX_CFG5,
> + PHY_AUX_CFG6,
> + PHY_AUX_CFG7,
> + PHY_AUX_CFG8,
> + PHY_AUX_CFG9,
> + PHY_AUX_CFG_MAX,
> +};
> +
> +static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
> +{
> + switch (cfg_type) {
> + case PHY_AUX_CFG0:
> + return DP_ENUM_STR(PHY_AUX_CFG0);
> + case PHY_AUX_CFG1:
> + return DP_ENUM_STR(PHY_AUX_CFG1);
> + case PHY_AUX_CFG2:
> + return DP_ENUM_STR(PHY_AUX_CFG2);
> + case PHY_AUX_CFG3:
> + return DP_ENUM_STR(PHY_AUX_CFG3);
> + case PHY_AUX_CFG4:
> + return DP_ENUM_STR(PHY_AUX_CFG4);
> + case PHY_AUX_CFG5:
> + return DP_ENUM_STR(PHY_AUX_CFG5);
> + case PHY_AUX_CFG6:
> + return DP_ENUM_STR(PHY_AUX_CFG6);
> + case PHY_AUX_CFG7:
> + return DP_ENUM_STR(PHY_AUX_CFG7);
> + case PHY_AUX_CFG8:
> + return DP_ENUM_STR(PHY_AUX_CFG8);
> + case PHY_AUX_CFG9:
> + return DP_ENUM_STR(PHY_AUX_CFG9);
> + default:
> + return "unknown";
> + }
> +}
> +
> +/**
> + * struct dp_parser - DP parser's data exposed to clients
> + *
> + * @pdev: platform data of the client
> + * @mp: gpio, regulator and clock related data
> + * @pinctrl: pin-control related data
> + * @disp_data: controller's display related data
> + * @parse: function to be called by client to parse device tree.
> + */
> +struct dp_parser {
> + struct platform_device *pdev;
> + struct dss_module_power mp[DP_MAX_PM];
> + struct dp_pinctrl pinctrl;
> + struct dp_io io;
> + struct dp_display_data disp_data;
> +
> + u8 l_map[4];
> + struct dp_aux_cfg aux_cfg[AUX_CFG_LEN];
> + u32 max_pclk_khz;
> +
> + int (*parse)(struct dp_parser *parser);
> +};
> +
> +/**
> + * dp_parser_get() - get the DP's device tree parser module
> + *
> + * @pdev: platform data of the client
> + * return: pointer to dp_parser structure.
> + *
> + * This function provides client capability to parse the
> + * device tree and populate the data structures. The data
> + * related to clock, regulators, pin-control and other
> + * can be parsed using this module.
> + */
> +struct dp_parser *dp_parser_get(struct platform_device *pdev);
> +
> +/**
> + * dp_parser_put() - cleans the dp_parser module
> + *
> + * @parser: pointer to the parser's data.
> + */
> +void dp_parser_put(struct dp_parser *parser);
> +#endif
> diff --git a/drivers/gpu/drm/msm/dp/dp_power.c b/drivers/gpu/drm/msm/dp/dp_power.c
> new file mode 100644
> index 0000000..1d58480
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/dp_power.c
> @@ -0,0 +1,599 @@
> +/*
> + * Copyright (c) 2012-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.
> + *
> + */
SPDX please.
> +#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
> +
> +#include <linux/clk.h>
> +#include "dp_power.h"
> +
> +#define DP_CLIENT_NAME_SIZE 20
> +
> +struct dp_power_private {
> + struct dp_parser *parser;
> + struct platform_device *pdev;
> + struct clk *pixel_clk_rcg;
> + struct clk *pixel_parent;
> +
> + struct dp_power dp_power;
> +
> + bool core_clks_on;
> + bool link_clks_on;
> +};
> +
> +static int dp_power_regulator_init(struct dp_power_private *power)
> +{
> + int rc = 0, i = 0, j = 0;
> + struct platform_device *pdev;
> + struct dp_parser *parser;
> +
> + parser = power->parser;
> + pdev = power->pdev;
> +
> + for (i = DP_CORE_PM; !rc && (i < DP_MAX_PM); i++) {
> + rc = msm_dss_config_vreg(&pdev->dev,
> + parser->mp[i].vreg_config,
> + parser->mp[i].num_vreg, 1);
> + if (rc) {
> + pr_err("failed to init vregs for %s\n",
> + dp_parser_pm_name(i));
msm_dss_config_vreg() already prints lots of error messages for you.
> + for (j = i - 1; j >= DP_CORE_PM; j--) {
> + msm_dss_config_vreg(&pdev->dev,
> + parser->mp[j].vreg_config,
> + parser->mp[j].num_vreg, 0);
> + }
> +
> + goto error;
just return rc;
> + }
> + }
> +error:
> + return rc;
> +}
> +
> +static void dp_power_regulator_deinit(struct dp_power_private *power)
> +{
> + int rc = 0, i = 0;
> + struct platform_device *pdev;
> + struct dp_parser *parser;
> +
> + parser = power->parser;
> + pdev = power->pdev;
> +
> + for (i = DP_CORE_PM; (i < DP_MAX_PM); i++) {
> + rc = msm_dss_config_vreg(&pdev->dev,
> + parser->mp[i].vreg_config,
> + parser->mp[i].num_vreg, 0);
> + if (rc)
> + pr_err("failed to deinit vregs for %s\n",
> + dp_parser_pm_name(i));
Same.
> + }
> +}
> +
> +static int dp_power_regulator_ctrl(struct dp_power_private *power, bool enable)
> +{
> + int rc = 0, i = 0, j = 0;
> + struct dp_parser *parser;
> +
> + parser = power->parser;
> +
> + for (i = DP_CORE_PM; i < DP_MAX_PM; i++) {
> + rc = msm_dss_enable_vreg(
> + parser->mp[i].vreg_config,
> + parser->mp[i].num_vreg, enable);
> + if (rc) {
> + pr_err("failed to '%s' vregs for %s\n",
> + enable ? "enable" : "disable",
> + dp_parser_pm_name(i));
msm_dss_enable_vreg() already gives you the errors you need.
> + if (enable) {
> + for (j = i-1; j >= DP_CORE_PM; j--) {
> + msm_dss_enable_vreg(
> + parser->mp[j].vreg_config,
> + parser->mp[j].num_vreg, 0);
> + }
> + }
> + goto error;
> + }
> + }
> +error:
> + return rc;
You don't need a label with a single return in it.
> +}
> +
> +static int dp_power_pinctrl_set(struct dp_power_private *power, bool active)
> +{
> + int rc = -EFAULT;
> + struct pinctrl_state *pin_state;
> + struct dp_parser *parser;
> +
> + parser = power->parser;
> +
> + if (IS_ERR_OR_NULL(parser->pinctrl.pin))
Recall that pinctrl.in cannot be NULL.
> + return PTR_ERR(parser->pinctrl.pin);
> +
> + pin_state = active ? parser->pinctrl.state_active
> + : parser->pinctrl.state_suspend;
> + if (!IS_ERR_OR_NULL(pin_state)) {
Same
> + rc = pinctrl_select_state(parser->pinctrl.pin,
> + pin_state);
> + if (rc)
> + pr_err("can not set %s pins\n",
> + active ? "dp_active"
> + : "dp_sleep");
> + } else {
> + pr_err("invalid '%s' pinstate\n",
> + active ? "dp_active"
> + : "dp_sleep");
You already let the user know during inint that the pin_state was not
correct. You don't need to update them again every time you try to change it.
> + }
> +
> + return rc;
> +}
> +
> +static int dp_power_clk_init(struct dp_power_private *power, bool enable)
> +{
> + int rc = 0;
> + struct dss_module_power *core, *ctrl;
> + struct device *dev;
> +
> + core = &power->parser->mp[DP_CORE_PM];
> + ctrl = &power->parser->mp[DP_CTRL_PM];
> +
> + dev = &power->pdev->dev;
> +
> + if (!core || !ctrl) {
> + pr_err("invalid power_data\n");
> + rc = -EINVAL;
> + goto exit;
> + }
These are both impossible since you are deriving the pointer above.
> +
> + if (enable) {
> + rc = msm_dss_get_clk(dev, core->clk_config, core->num_clk);
> + if (rc) {
> + pr_err("failed to get %s clk. err=%d\n",
> + dp_parser_pm_name(DP_CORE_PM), rc);
msm_dss_get_clk already prints an error message for you.
> + goto exit;
> + }
> +
> + rc = msm_dss_get_clk(dev, ctrl->clk_config, ctrl->num_clk);
> + if (rc) {
> + pr_err("failed to get %s clk. err=%d\n",
> + dp_parser_pm_name(DP_CTRL_PM), rc);
Same
> + goto ctrl_get_error;
> + }
> +
> + power->pixel_clk_rcg = devm_clk_get(dev, "pixel_clk_rcg");
> + if (IS_ERR(power->pixel_clk_rcg)) {
> + pr_debug("Unable to get DP pixel clk RCG\n");
This seems like it should be an error?
> + power->pixel_clk_rcg = NULL;
> + }
> +
> + power->pixel_parent = devm_clk_get(dev, "pixel_parent");
> + if (IS_ERR(power->pixel_parent)) {
> + pr_debug("Unable to get DP pixel RCG parent\n");
Same?
> + power->pixel_parent = NULL;
> + }
> + } else {
This is another one of those functions that shares very little with the other
side and can be easily split into a separate function that is easier to
understand.
> + if (power->pixel_parent)
> + devm_clk_put(dev, power->pixel_parent);
> +
> + if (power->pixel_clk_rcg)
> + devm_clk_put(dev, power->pixel_clk_rcg);
You don't need to put managed clocks.
> +
> + msm_dss_put_clk(ctrl->clk_config, ctrl->num_clk);
> + msm_dss_put_clk(core->clk_config, core->num_clk);
> + }
> +
> + return rc;
> +
> +ctrl_get_error:
> + msm_dss_put_clk(core->clk_config, core->num_clk);
> +exit:
> + return rc;
> +}
> +
> +static int dp_power_clk_set_rate(struct dp_power_private *power,
> + enum dp_pm_type module, bool enable)
> +{
> + int rc = 0;
> + struct dss_module_power *mp;
> +
> + if (!power) {
> + pr_err("invalid power data\n");
> + rc = -EINVAL;
> + goto exit;
> + }
power will always be valid.
> + mp = &power->parser->mp[module];
> +
> + if (enable) {
> + rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
> + if (rc) {
> + pr_err("failed to set clks rate.\n");
msm_dss_clk_set_rate() already gives you an error message;
> + goto exit;
> + }
> +
> + rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, 1);
> + if (rc) {
> + pr_err("failed to enable clks\n");
Same.
> + goto exit;
> + }
> + } else {
> + rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, 0);
> + if (rc) {
> + pr_err("failed to disable clks\n");
Same
> + goto exit;
Suspect indentation, but as usual, don't use a single return in a label - just
return directly.
> + }
> + }
> +exit:
> + return rc;
> +}
> +
> +static int dp_power_clk_enable(struct dp_power *dp_power,
> + enum dp_pm_type pm_type, bool enable)
> +{
> + int rc = 0;
> + struct dss_module_power *mp;
> + struct dp_power_private *power;
> +
> + if (!dp_power) {
> + pr_err("invalid power data\n");
> + rc = -EINVAL;
> + goto error;
> + }
dp_power will always be valid.
> + power = container_of(dp_power, struct dp_power_private, dp_power);
> +
> + mp = &power->parser->mp[pm_type];
> +
> + if ((pm_type != DP_CORE_PM) && (pm_type != DP_CTRL_PM)) {
> + pr_err("unsupported power module: %s\n",
> + dp_parser_pm_name(pm_type));
This seems like it would be never be true outside of egregious developer
error. - I suppose you could WARN_ON here but it just doesn't seem like a thing.
> + return -EINVAL;
> + }
> +
> + if (enable) {
> + if ((pm_type == DP_CORE_PM)
> + && (power->core_clks_on)) {
> + pr_debug("core clks already enabled\n");
> + return 0;
> + }
> +
> + if ((pm_type == DP_CTRL_PM)
> + && (power->link_clks_on)) {
> + pr_debug("links clks already enabled\n");
> + return 0;
> + }
> +
> + if ((pm_type == DP_CTRL_PM) && (!power->core_clks_on)) {
> + pr_debug("Need to enable core clks before link clks\n");
> +
> + rc = dp_power_clk_set_rate(power, DP_CORE_PM, enable);
> + if (rc) {
> + pr_err("failed to enable clks: %s. err=%d\n",
> + dp_parser_pm_name(DP_CORE_PM), rc);
> + goto error;
> + } else {
> + power->core_clks_on = true;
> + }
> + }
> + }
> +
> + rc = dp_power_clk_set_rate(power, pm_type, enable);
> + if (rc) {
> + pr_err("failed to '%s' clks for: %s. err=%d\n",
> + enable ? "enable" : "disable",
> + dp_parser_pm_name(pm_type), rc);
> + goto error;
> + }
> +
> + if (pm_type == DP_CORE_PM)
> + power->core_clks_on = enable;
> + else
> + power->link_clks_on = enable;
With the number of if statements in here it almost seems more logical to have a
separate function for both DP_CORE_PM and DP_CTRL_PM - yeah, it would copy a bit
of code but it would be a heck of a lot more readable.
> + pr_debug("%s clocks for %s\n",
> + enable ? "enable" : "disable",
> + dp_parser_pm_name(pm_type));
> + pr_debug("link_clks:%s core_clks:%s\n",
> + power->link_clks_on ? "on" : "off",
> + power->core_clks_on ? "on" : "off");
> +error:
> + return rc;
> +}
> +
> +static int dp_power_request_gpios(struct dp_power_private *power)
> +{
> + int rc = 0, i;
> + struct device *dev;
> + struct dss_module_power *mp;
> + static const char * const gpio_names[] = {
> + "aux_enable", "aux_sel", "usbplug_cc",
> + };
> +
> + if (!power) {
> + pr_err("invalid power data\n");
> + return -EINVAL;
> + }
> +
> + dev = &power->pdev->dev;
> + mp = &power->parser->mp[DP_CORE_PM];
> +
> + for (i = 0; i < ARRAY_SIZE(gpio_names); i++) {
> + unsigned int gpio = mp->gpio_config[i].gpio;
> +
> + if (gpio_is_valid(gpio)) {
> + rc = devm_gpio_request(dev, gpio, gpio_names[i]);
> + if (rc) {
> + pr_err("request %s gpio failed, rc=%d\n",
> + gpio_names[i], rc);
> + goto error;
> + }
> + }
> + }
> + return 0;
> +error:
> + for (i = 0; i < ARRAY_SIZE(gpio_names); i++) {
> + unsigned int gpio = mp->gpio_config[i].gpio;
> +
> + if (gpio_is_valid(gpio))
> + gpio_free(gpio);
Can you call gpio_free() on a device managed resource? I think you probably want
devm_gpio_free.
> + }
> + return rc;
> +}
> +
> +static bool dp_power_find_gpio(const char *gpio1, const char *gpio2)
> +{
> + return !!strnstr(gpio1, gpio2, strlen(gpio1));
> +}
> +
> +static void dp_power_set_gpio(struct dp_power_private *power, bool flip)
> +{
> + int i;
> + struct dss_module_power *mp = &power->parser->mp[DP_CORE_PM];
> + struct dss_gpio *config = mp->gpio_config;
> +
> + for (i = 0; i < mp->num_gpio; i++) {
> + if (dp_power_find_gpio(config->gpio_name, "aux-sel"))
I think config->gpio_name is the name from the DT, basically qcom,aux-sel-gpio?
Why not just strcmp on that instead of going the much harder route
> + config->value = flip;
> +
> + if (gpio_is_valid(config->gpio)) {
> + pr_debug("gpio %s, value %d\n", config->gpio_name,
> + config->value);
> +
> + if (dp_power_find_gpio(config->gpio_name, "aux-en") ||
> + dp_power_find_gpio(config->gpio_name, "aux-sel"))
Same and same
> + gpio_direction_output(config->gpio,
> + config->value);
> + else
> + gpio_set_value(config->gpio, config->value);
> +
> + }
> + config++;
> + }
> +}
> +
> +static int dp_power_config_gpios(struct dp_power_private *power, bool flip,
> + bool enable)
> +{
> + int rc = 0, i;
> + struct dss_module_power *mp;
> + struct dss_gpio *config;
> +
> + mp = &power->parser->mp[DP_CORE_PM];
> + config = mp->gpio_config;
> +
> + if (enable) {
> + rc = dp_power_request_gpios(power);
> + if (rc) {
> + pr_err("gpio request failed\n");
dp_power_request_gpios prints an error message on every failure point.
> + return rc;
> + }
> +
> + dp_power_set_gpio(power, flip);
It seems like maybe dp_power_request_gpios() and dp_power_set_gpio() can be
combined - both of them only get called here.
> + } else {
Again, this feels like it could safely be in its own function for clarity.
> + for (i = 0; i < mp->num_gpio; i++) {
> + gpio_set_value(config[i].gpio, 0);
> + gpio_free(config[i].gpio);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int dp_power_client_init(struct dp_power *dp_power)
> +{
> + int rc = 0;
> + struct dp_power_private *power;
> +
> + if (!dp_power) {
> + pr_err("invalid power data\n");
> + return -EINVAL;
> + }
dp_power can never be NULL here.
> + power = container_of(dp_power, struct dp_power_private, dp_power);
> +
> + pm_runtime_enable(&power->pdev->dev);
> +
> + rc = dp_power_regulator_init(power);
> + if (rc) {
> + pr_err("failed to init regulators\n");
The sub function already prints error messages.
> + goto error_power;
> + }
> +
> + rc = dp_power_clk_init(power, true);
> + if (rc) {
> + pr_err("failed to init clocks\n");
Also this one.
> + goto error_clk;
> + }
> + return 0;
You can flip this if statement and save a goto:
if (!rc)
return 0;
dp_power_regulator_deinit(power);
...
> +
> +error_clk:
> + dp_power_regulator_deinit(power);
> +error_power:
> + pm_runtime_disable(&power->pdev->dev);
> + return rc;
> +}
> +
> +static void dp_power_client_deinit(struct dp_power *dp_power)
> +{
> + struct dp_power_private *power;
> +
> + if (!dp_power) {
> + pr_err("invalid power data\n");
> + return;
> + }
dp_power is always set.
> + power = container_of(dp_power, struct dp_power_private, dp_power);
> +
> + dp_power_clk_init(power, false);
> + dp_power_regulator_deinit(power);
> + pm_runtime_disable(&power->pdev->dev);
> +
> +}
> +
> +static int dp_power_set_pixel_clk_parent(struct dp_power *dp_power)
> +{
> + int rc = 0;
> + struct dp_power_private *power;
> +
> + if (!dp_power) {
> + pr_err("invalid power data\n");
> + rc = -EINVAL;
> + goto exit;
> + }
>
dp_power is always set.
> + power = container_of(dp_power, struct dp_power_private, dp_power);
> +
> + if (power->pixel_clk_rcg && power->pixel_parent)
> + clk_set_parent(power->pixel_clk_rcg, power->pixel_parent);
> +exit:
> + return rc;
> +}
> +
> +static int dp_power_init(struct dp_power *dp_power, bool flip)
> +{
> + int rc = 0;
> + struct dp_power_private *power;
> +
> + if (!dp_power) {
> + pr_err("invalid power data\n");
> + rc = -EINVAL;
> + goto exit;
> + }
dp_power is always set.
> + power = container_of(dp_power, struct dp_power_private, dp_power);
> +
> + pm_runtime_get_sync(&power->pdev->dev);
> + rc = dp_power_regulator_ctrl(power, true);
> + if (rc) {
> + pr_err("failed to enable regulators\n");
This already prints the error messages you need.
> + goto exit;
> + }
> +
> + rc = dp_power_pinctrl_set(power, true);
> + if (rc) {
> + pr_err("failed to set pinctrl state\n");
Same.
> + goto err_pinctrl;
> + }
> +
> + rc = dp_power_config_gpios(power, flip, true);
> + if (rc) {
> + pr_err("failed to enable gpios\n");
Same.
> + goto err_gpio;
> + }
> +
> + rc = dp_power_clk_enable(dp_power, DP_CORE_PM, true);
> + if (rc) {
> + pr_err("failed to enable DP core clocks\n");
Same.
> + goto err_clk;
> + }
> +
> + return 0;
As above, flip the if statement and save a goto:
if (!rc)
return 0;
dp_power_config_gpios(power, flip, false);
...
> +
> +err_clk:
> + dp_power_config_gpios(power, flip, false);
> +err_gpio:
> + dp_power_pinctrl_set(power, false);
> +err_pinctrl:
> + dp_power_regulator_ctrl(power, false);
> +exit:
> + pm_runtime_put_sync(&power->pdev->dev);
> + return rc;
> +}
> +
> +static int dp_power_deinit(struct dp_power *dp_power)
> +{
> + int rc = 0;
> + struct dp_power_private *power;
> +
> + if (!dp_power) {
> + pr_err("invalid power data\n");
> + rc = -EINVAL;
> + goto exit;
> + }
dp_power can never be NULL;
> + power = container_of(dp_power, struct dp_power_private, dp_power);
> +
> + dp_power_clk_enable(dp_power, DP_CORE_PM, false);
> + dp_power_config_gpios(power, false, false);
> + dp_power_pinctrl_set(power, false);
> + dp_power_regulator_ctrl(power, false);
> + pm_runtime_put_sync(&power->pdev->dev);
> +exit:
> + return rc;
> +}
> +
> +struct dp_power *dp_power_get(struct dp_parser *parser)
> +{
> + int rc = 0;
> + struct dp_power_private *power;
> + struct dp_power *dp_power;
> +
> + if (!parser) {
> + pr_err("invalid input\n");
> + rc = -EINVAL;
> + goto error;
> + }
parser can never be NULL;
> + power = devm_kzalloc(&parser->pdev->dev, sizeof(*power), GFP_KERNEL);
> + if (!power) {
> + rc = -ENOMEM;
Just return ERR_PTR(-ENOMEM);
> + goto error;
> + }
> +
> + power->parser = parser;
> + power->pdev = parser->pdev;
> +
> + dp_power = &power->dp_power;
> +
> + dp_power->init = dp_power_init;
> + dp_power->deinit = dp_power_deinit;
> + dp_power->clk_enable = dp_power_clk_enable;
> + dp_power->set_pixel_clk_parent = dp_power_set_pixel_clk_parent;
> + dp_power->power_client_init = dp_power_client_init;
> + dp_power->power_client_deinit = dp_power_client_deinit;
> +
> + return dp_power;
> +error:
> + return ERR_PTR(rc);
> +}
> +
> +void dp_power_put(struct dp_power *dp_power)
> +{
> + struct dp_power_private *power = NULL;
> +
> + if (!dp_power)
> + return;
> +
> + power = container_of(dp_power, struct dp_power_private, dp_power);
> +
> + devm_kfree(&power->pdev->dev, power);
You don't need to kfree device managed mmemory.
> +}
> diff --git a/drivers/gpu/drm/msm/dp/dp_power.h b/drivers/gpu/drm/msm/dp/dp_power.h
> new file mode 100644
> index 0000000..d1adaaf
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/dp_power.h
> @@ -0,0 +1,57 @@
> +/*
> + * Copyright (c) 2012-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.
> + *
> + */
SPDX please.
> +
> +#ifndef _DP_POWER_H_
> +#define _DP_POWER_H_
> +
> +#include "dp_parser.h"
> +#include "dpu_power_handle.h"
> +
> +/**
> + * sruct dp_power - DisplayPort's power related data
> + *
> + * @init: initializes the regulators/core clocks/GPIOs/pinctrl
> + * @deinit: turns off the regulators/core clocks/GPIOs/pinctrl
> + * @clk_enable: enable/disable the DP clocks
> + * @set_pixel_clk_parent: set the parent of DP pixel clock
> + */
> +struct dp_power {
> + int (*init)(struct dp_power *power, bool flip);
> + int (*deinit)(struct dp_power *power);
> + int (*clk_enable)(struct dp_power *power, enum dp_pm_type pm_type,
> + bool enable);
> + int (*set_pixel_clk_parent)(struct dp_power *power);
> + int (*power_client_init)(struct dp_power *power);
> + void (*power_client_deinit)(struct dp_power *power);
> +};
> +
> +/**
> + * dp_power_get() - configure and get the DisplayPort power module data
> + *
> + * @parser: instance of parser module
> + * return: pointer to allocated power module data
> + *
> + * This API will configure the DisplayPort's power module and provides
> + * methods to be called by the client to configure the power related
> + * modueles.
> + */
> +struct dp_power *dp_power_get(struct dp_parser *parser);
> +
> +/**
> + * dp_power_put() - release the power related resources
> + *
> + * @power: pointer to the power module's data
> + */
> +void dp_power_put(struct dp_power *power);
> +#endif /* _DP_POWER_H_ */
> diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
> new file mode 100644
> index 0000000..77b5c0e
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/dp_reg.h
> @@ -0,0 +1,357 @@
> +/*
> + * Copyright (c) 2017-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.
> + *
> + */
SPDX please.
<snip the rest>
Jordan
--
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
More information about the Freedreno
mailing list