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@xxxxxxxxxxxxxx> > --- > 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 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel