On Wed, Oct 10, 2018 at 10:15:59AM -0700, Chandan Uddaraju wrote:
> Add the needed DP PLL specific files to support
> display port interface on msm targets.
> 
> The DP driver calls the DP PLL driver registration.
> The DP driver sets the link and pixel clock sources.
> 
> Signed-off-by: Chandan Uddaraju <chand...@codeaurora.org>
> ---
>  drivers/gpu/drm/msm/Kconfig                   |  16 +
>  drivers/gpu/drm/msm/Makefile                  |   6 +
>  drivers/gpu/drm/msm/dp/dp_ctrl.c              |   1 +
>  drivers/gpu/drm/msm/dp/dp_display.c           |  50 +++
>  drivers/gpu/drm/msm/dp/dp_display.h           |   3 +
>  drivers/gpu/drm/msm/dp/dp_parser.h            |   3 +
>  drivers/gpu/drm/msm/dp/dp_power.c             |  77 +++-
>  drivers/gpu/drm/msm/dp/dp_power.h             |   2 +
>  drivers/gpu/drm/msm/dp/pll/dp_pll.c           | 153 ++++++++
>  drivers/gpu/drm/msm/dp/pll/dp_pll.h           |  64 ++++
>  drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c      | 401 +++++++++++++++++++
>  drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h      |  94 +++++
>  drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c | 531 
> ++++++++++++++++++++++++++
>  13 files changed, 1389 insertions(+), 12 deletions(-)
>  create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.c
>  create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.h
>  create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c
>  create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h
>  create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c
> 
> diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
> index c363f24..1e0b9158 100644
> --- a/drivers/gpu/drm/msm/Kconfig
> +++ b/drivers/gpu/drm/msm/Kconfig
> @@ -58,6 +58,22 @@ config DRM_MSM_DP
>         driver. DP external display support is enabled
>         through this config option. It can be primary or
>         secondary display on device.
> +
> +config DRM_MSM_DP_PLL
> +     bool "Enable DP PLL driver in MSM DRM"
> +     depends on DRM_MSM_DP && COMMON_CLK
> +     default y

The default should be n

> +     help
> +       Choose this option to enable DP PLL driver which provides DP
> +       source clocks under common clock framework.
> +
> +config DRM_MSM_DP_10NM_PLL
> +     bool "Enable DP 10nm PLL driver in MSM DRM (used by SDM845)"
> +     depends on DRM_MSM_DP

Should this be DRM_MSM_DP_PLL instead?


> +     default y

The default should be n

> +     help
> +       Choose this option if DP PLL on SDM845 is used on the platform.
> +
>  config DRM_MSM_DSI
>       bool "Enable DSI support in MSM DRM driver"
>       depends on DRM_MSM
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index 765a8d8..8d18353 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -137,4 +137,10 @@ msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += 
> dsi/pll/dsi_pll_14nm.o
>  msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/pll/dsi_pll_10nm.o
>  endif
>  
> +ifeq ($(CONFIG_DRM_MSM_DP_PLL),y)
> +msm-y += dp/pll/dp_pll.o
> +msm-y += dp/pll/dp_pll_10nm.o
> +msm-y += dp/pll/dp_pll_10nm_util.o

Instead of the ifeq, these should follow the form:

msm-$(CONFIG_BLAH) +=

> +endif
> +
>  obj-$(CONFIG_DRM_MSM)        += msm.o
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c 
> b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index 08a52f5..e23beee 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -1051,6 +1051,7 @@ static int dp_ctrl_enable_mainlink_clocks(struct 
> dp_ctrl_private *ctrl)
>  {
>       int ret = 0;
>  
> +     ctrl->power->set_link_clk_parent(ctrl->power);
>       ctrl->power->set_pixel_clk_parent(ctrl->power);
>  
>       dp_ctrl_set_clock_rate(ctrl, "ctrl_link_clk",
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c 
> b/drivers/gpu/drm/msm/dp/dp_display.c
> index 8c98399..2bf6635 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -72,6 +72,48 @@ struct dp_display_private {
>       {}
>  };
>  
> +static int dp_get_pll(struct dp_display_private *dp_priv)
> +{
> +     struct platform_device *pdev = NULL;
> +     struct platform_device *pll_pdev;
> +     struct device_node *pll_node;
> +     struct dp_parser *dp_parser = NULL;
> +
> +     if (!dp_priv) {
> +             pr_err("Invalid Arguments\n");
> +             return -EINVAL;
> +     }
> +
> +     pdev = dp_priv->pdev;
> +     dp_parser = dp_priv->parser;
> +
> +     if (!dp_parser) {
> +             pr_err("Parser not initialized.\n");
> +             return -EINVAL;
> +     }

Can we please remove the unnecessary null checks from this patch too?

> +
> +     pll_node = of_parse_phandle(pdev->dev.of_node, "pll-node", 0);
> +     if (!pll_node) {
> +             dev_err(&pdev->dev, "cannot find pll device\n");
> +             return -ENXIO;
> +     }
> +
> +     pll_pdev = of_find_device_by_node(pll_node);
> +     if (pll_pdev)
> +             dp_parser->pll = platform_get_drvdata(pll_pdev);

What if the pll driver goes away before the dp driver?

> +
> +     of_node_put(pll_node);
> +
> +     if (!pll_pdev || !dp_parser->pll) {
> +             dev_err(&pdev->dev, "%s: pll driver is not ready\n", __func__);

This patch (and even just this function) has both pr_err and dev_err, oy!

Please convert everything to one logging medium. The msm driver was recently
converted to DRM_DEV_*, so let's go with that for all of msm/dp/*


> +             return -EPROBE_DEFER;
> +     }
> +
> +     dp_parser->pll_dev = get_device(&pll_pdev->dev);
> +
> +     return 0;
> +}
> +
>  static irqreturn_t dp_display_irq(int irq, void *dev_id)
>  {
>       struct dp_display_private *dp = dev_id;
> @@ -125,6 +167,12 @@ static int dp_display_bind(struct device *dev, struct 
> device *master,
>               goto end;
>       }
>  
> +     rc = dp_get_pll(dp);
> +     if (rc) {
> +             pr_err(" DP get PLL instance failed\n");

Print rc, here and everywhere else.

> +             goto end;
> +     }
> +
>       rc = dp->aux->drm_aux_register(dp->aux);
>       if (rc) {
>               pr_err("DRM DP AUX register failed\n");
> @@ -921,6 +969,7 @@ int __init msm_dp_register(void)
>  {
>       int ret;
>  
> +     msm_dp_pll_driver_register();
>       ret = platform_driver_register(&dp_display_driver);
>       if (ret) {
>               pr_err("driver register failed");
> @@ -932,6 +981,7 @@ int __init msm_dp_register(void)
>  
>  void __exit msm_dp_unregister(void)
>  {
> +     msm_dp_pll_driver_unregister();
>       platform_driver_unregister(&dp_display_driver);
>  }
>  
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h 
> b/drivers/gpu/drm/msm/dp/dp_display.h
> index ca5eae5..9cd6a6b 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -52,4 +52,7 @@ struct msm_dp {
>  
>  int dp_display_get_num_of_displays(void);
>  int dp_display_get_displays(void **displays, int count);
> +void __init msm_dp_pll_driver_register(void);
> +void __exit msm_dp_pll_driver_unregister(void);
> +
>  #endif /* _DP_DISPLAY_H_ */
> diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h 
> b/drivers/gpu/drm/msm/dp/dp_parser.h
> index b39ea02..372997e 100644
> --- a/drivers/gpu/drm/msm/dp/dp_parser.h
> +++ b/drivers/gpu/drm/msm/dp/dp_parser.h
> @@ -16,6 +16,7 @@
>  #define _DP_PARSER_H_
>  
>  #include "dpu_io_util.h"
> +#include "pll/dp_pll.h"
>  
>  #define DP_LABEL "MDSS DP DISPLAY"
>  #define AUX_CFG_LEN  10
> @@ -175,6 +176,8 @@ struct dp_parser {
>       struct dp_pinctrl pinctrl;
>       struct dp_io io;
>       struct dp_display_data disp_data;
> +     struct msm_dp_pll *pll;
> +     struct device *pll_dev;

Store pll_dev in msm_dp_pll as 'dev' and remove this?

>  
>       u8 l_map[4];
>       struct dp_aux_cfg aux_cfg[AUX_CFG_LEN];
> diff --git a/drivers/gpu/drm/msm/dp/dp_power.c 
> b/drivers/gpu/drm/msm/dp/dp_power.c
> index 1d58480..3a1679c 100644
> --- a/drivers/gpu/drm/msm/dp/dp_power.c
> +++ b/drivers/gpu/drm/msm/dp/dp_power.c
> @@ -23,7 +23,9 @@ struct dp_power_private {
>       struct dp_parser *parser;
>       struct platform_device *pdev;
>       struct clk *pixel_clk_rcg;
> -     struct clk *pixel_parent;
> +     struct clk *link_clk_src;
> +     struct clk *pixel_provider;
> +     struct clk *link_provider;
>  
>       struct dp_power dp_power;
>  
> @@ -154,6 +156,16 @@ static int dp_power_clk_init(struct dp_power_private 
> *power, bool enable)
>               goto exit;
>       }
>  
> +     if (power->parser->pll && power->parser->pll->get_provider) {
> +             rc = power->parser->pll->get_provider(power->parser->pll,
> +                             &power->link_provider, &power->pixel_provider);

Hopefully this gets simplified when you de-abstract the rest of the dp driver.

> +             if (rc) {
> +                     pr_info("%s: can't get provider from pll, don't set 
> parent\n",
> +                             __func__);
> +                     return 0;
> +             }
> +     }
> +
>       if (enable) {
>               rc = msm_dss_get_clk(dev, core->clk_config, core->num_clk);
>               if (rc) {
> @@ -173,17 +185,10 @@ static int dp_power_clk_init(struct dp_power_private 
> *power, bool enable)
>               if (IS_ERR(power->pixel_clk_rcg)) {
>                       pr_debug("Unable to get DP pixel clk RCG\n");
>                       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");
> -                     power->pixel_parent = NULL;
> +                     rc = -ENODEV;
> +                     goto ctrl_get_error;
>               }
>       } else {
> -             if (power->pixel_parent)
> -                     devm_clk_put(dev, power->pixel_parent);
> -
>               if (power->pixel_clk_rcg)
>                       devm_clk_put(dev, power->pixel_clk_rcg);
>  
> @@ -459,6 +464,49 @@ static void dp_power_client_deinit(struct dp_power 
> *dp_power)
>  
>  }
>  
> +static int dp_power_set_link_clk_parent(struct dp_power *dp_power)
> +{
> +     int rc = 0;
> +     struct dp_power_private *power;
> +     u32 num;
> +     struct dss_clk *cfg;
> +     char *name = "ctrl_link_clk";
> +
> +     if (!dp_power) {
> +             pr_err("invalid power data\n");
> +             rc = -EINVAL;
> +             goto exit;
> +     }
> +
> +     power = container_of(dp_power, struct dp_power_private, dp_power);
> +
> +     num = power->parser->mp[DP_CTRL_PM].num_clk;
> +     cfg = power->parser->mp[DP_CTRL_PM].clk_config;
> +
> +     while (num && strcmp(cfg->clk_name, name)) {

I think I commented on this in the other patch, but don't use strings to do
lookups, please just store the pointer directly if you need a reference.

> +             num--;
> +             cfg++;
> +     }
> +
> +     if (num && power->link_provider) {
> +             power->link_clk_src = clk_get_parent(cfg->clk);

Check return for ERR_PTR

> +                     if (power->link_clk_src) {

Indenting is wrong on this block

> +                             clk_set_parent(power->link_clk_src, 
> power->link_provider);
> +                             pr_debug("%s: is the parent of clk=%s\n",
> +                                             
> __clk_get_name(power->link_provider),
> +                                             
> __clk_get_name(power->link_clk_src));
> +                     } else {
> +                             pr_err("couldn't get parent for clk=%s\n", 
> name);
> +                             rc = -EINVAL;

Make thi:

        if (!power->link_clk_src) {
                DRM_DEV_ERROR(...)
                return -EINVAL;
        }

        ret = clk_set_parent(power->link_clk_src, power->link_provider);
        if (ret) {
                /* propertly handle error */
        }
        pr_debug("%s: is the parent of clk=%s\n",
                 __clk_get_name(power->link_provider),
                 __clk_get_name(power->link_clk_src));


> +                     }
> +     } else {
> +             pr_err("%s clock could not be set parent\n", name);
> +             rc = -EINVAL;
> +     }

Same thing here, flip the if condition to check for error and return early, thus
saving yourself a level of indentation for the successful case.

> +exit:
> +     return rc;

Remove exit label and return directly above

> +}
> +
>  static int dp_power_set_pixel_clk_parent(struct dp_power *dp_power)
>  {
>       int rc = 0;
> @@ -472,8 +520,12 @@ static int dp_power_set_pixel_clk_parent(struct dp_power 
> *dp_power)
>  
>       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);
> +     if (power->pixel_clk_rcg && power->pixel_provider) {
> +             clk_set_parent(power->pixel_clk_rcg, power->pixel_provider);

s/pixel_parent/pixel_provider/ isn't related to this change, can you please use
the final name in the main dp patch so it's correct in this one?

> +             pr_debug("%s: is the parent of clk=%s\n",
> +                                     __clk_get_name(power->pixel_provider),
> +                                     __clk_get_name(power->pixel_clk_rcg));

Same comment here, this debug can go in the main patch.

> +     }
>  exit:
>       return rc;
>  }
> @@ -577,6 +629,7 @@ struct dp_power *dp_power_get(struct dp_parser *parser)
>       dp_power->init = dp_power_init;
>       dp_power->deinit = dp_power_deinit;
>       dp_power->clk_enable = dp_power_clk_enable;
> +     dp_power->set_link_clk_parent = dp_power_set_link_clk_parent;

Same comment here regarding the unnecessary indirection, just call this thing
directly where applicable.

>       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;
> diff --git a/drivers/gpu/drm/msm/dp/dp_power.h 
> b/drivers/gpu/drm/msm/dp/dp_power.h
> index d1adaaf..5effd32 100644
> --- a/drivers/gpu/drm/msm/dp/dp_power.h
> +++ b/drivers/gpu/drm/msm/dp/dp_power.h
> @@ -24,6 +24,7 @@
>   * @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_link_clk_parent: set the parent of DP link clock
>   * @set_pixel_clk_parent: set the parent of DP pixel clock
>   */
>  struct dp_power {
> @@ -31,6 +32,7 @@ struct dp_power {
>       int (*deinit)(struct dp_power *power);
>       int (*clk_enable)(struct dp_power *power, enum dp_pm_type pm_type,
>                               bool enable);
> +     int (*set_link_clk_parent)(struct dp_power *power);
>       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);
> diff --git a/drivers/gpu/drm/msm/dp/pll/dp_pll.c 
> b/drivers/gpu/drm/msm/dp/pll/dp_pll.c
> new file mode 100644
> index 0000000..a8612b5
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/pll/dp_pll.c
> @@ -0,0 +1,153 @@
> +/* Copyright (c) 2016-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 (and elsewhere).

> +
> +#include "dp_pll.h"
> +
> +int msm_dp_pll_util_parse_dt_clock(struct platform_device *pdev,
> +                                     struct msm_dp_pll *pll)
> +{
> +     u32 i = 0, rc = 0;
> +     struct dss_module_power *mp = &pll->mp;
> +     const char *clock_name;
> +     u32 clock_rate;
> +
> +     mp->num_clk = of_property_count_strings(pdev->dev.of_node,
> +                                                     "clock-names");
> +     if (mp->num_clk <= 0) {
> +             pr_err("clocks are not defined\n");
> +             goto clk_err;
> +     }
> +
> +     mp->clk_config = devm_kzalloc(&pdev->dev,
> +                     sizeof(struct dss_clk) * mp->num_clk, GFP_KERNEL);
> +     if (!mp->clk_config) {
> +             rc = -ENOMEM;
> +             mp->num_clk = 0;
> +             goto clk_err;
> +     }
> +
> +     for (i = 0; i < mp->num_clk; i++) {
> +             of_property_read_string_index(pdev->dev.of_node, "clock-names",
> +                                                     i, &clock_name);
> +             strlcpy(mp->clk_config[i].clk_name, clock_name,
> +                             sizeof(mp->clk_config[i].clk_name));
> +
> +             of_property_read_u32_index(pdev->dev.of_node, "clock-rate",
> +                                                     i, &clock_rate);
> +             mp->clk_config[i].rate = clock_rate;
> +
> +             if (!clock_rate)
> +                     mp->clk_config[i].type = DSS_CLK_AHB;
> +             else
> +                     mp->clk_config[i].type = DSS_CLK_PCLK;
> +     }
> +
> +clk_err:

remove clk_err and return directly above

> +     return rc;
> +}
> +
> +struct msm_dp_pll *msm_dp_pll_init(struct platform_device *pdev,

static please

> +                     enum msm_dp_pll_type type, int id)
> +{
> +     struct device *dev = &pdev->dev;
> +     struct msm_dp_pll *pll;
> +
> +     switch (type) {
> +     case MSM_DP_PLL_10NM:
> +             pll = msm_dp_pll_10nm_init(pdev, id);
> +             break;
> +     default:
> +             pll = ERR_PTR(-ENXIO);
> +             break;
> +     }
> +
> +     if (IS_ERR(pll)) {
> +             dev_err(dev, "%s: failed to init DP PLL\n", __func__);
> +             return pll;
> +     }
> +
> +     pll->type = type;

This should be set in the type-specific init function (ie:
msm_dp_pll_10nm_init()). That will let you simplify this entire function to:

{
        switch (type) {
        case MSM_DP_PLL_10NM:
                return msm_dp_pll_10nm_init(pdev, id);
        default:
                DRM_DEV_ERROR(&pdev->dev, "Invalid pll type: %d\n", type);
                return ERR_PTR(-ENXIO);
        }
}

> +
> +     DBG("DP:%d PLL registered", id);
> +
> +     return pll;
> +}
> +
> +static const struct of_device_id dp_pll_dt_match[] = {
> +#ifdef CONFIG_DRM_MSM_DP_10NM_PLL

I don't think you need this ifdef check, you've already provided a stub for the
init function

> +     { .compatible = "qcom,dp-pll-10nm" },
> +#endif
> +     {}
> +};
> +
> +static int dp_pll_driver_probe(struct platform_device *pdev)
> +{
> +     struct msm_dp_pll *pll;
> +     struct device *dev = &pdev->dev;
> +     const struct of_device_id *match;
> +     enum msm_dp_pll_type type;
> +
> +     match = of_match_node(dp_pll_dt_match, dev->of_node);
> +     if (!match)
> +             return -ENODEV;
> +
> +     if (!strcmp(match->compatible, "qcom,dp-pll-10nm"))
> +             type = MSM_DP_PLL_10NM;

This is what of_device_id->data is for, use it to store the type. Can you also
check the rest of the driver and do the same there?

> +     else
> +             type = MSM_DP_PLL_MAX;
> +
> +     pll = msm_dp_pll_init(pdev, type, 0);
> +     if (IS_ERR_OR_NULL(pll)) {
> +             dev_info(dev,

Why info level?

> +                     "%s: pll init failed: %ld, need separate pll clk 
> driver\n",
> +                     __func__, PTR_ERR(pll));
> +             return -ENODEV;
> +     }
> +
> +     platform_set_drvdata(pdev, pll);
> +
> +     return 0;
> +}
> +
> +static int dp_pll_driver_remove(struct platform_device *pdev)
> +{
> +     struct msm_dp_pll *pll = platform_get_drvdata(pdev);
> +
> +     if (pll) {
> +             //msm_dsi_pll_destroy(pll);

Hmm.

> +             pll = NULL;

No need to clear a local variable

> +     }
> +
> +     platform_set_drvdata(pdev, NULL);

You don't need to clear this either

> +
> +     return 0;
> +}
> +
> +static struct platform_driver dp_pll_platform_driver = {
> +     .probe      = dp_pll_driver_probe,
> +     .remove     = dp_pll_driver_remove,
> +     .driver     = {
> +             .name   = "msm_dp_pll",
> +             .of_match_table = dp_pll_dt_match,
> +     },
> +};
> +
> +void __init msm_dp_pll_driver_register(void)
> +{
> +     platform_driver_register(&dp_pll_platform_driver);
> +}
> +
> +void __exit msm_dp_pll_driver_unregister(void)
> +{
> +     platform_driver_unregister(&dp_pll_platform_driver);
> +}
> diff --git a/drivers/gpu/drm/msm/dp/pll/dp_pll.h 
> b/drivers/gpu/drm/msm/dp/pll/dp_pll.h
> new file mode 100644
> index 0000000..d52a81a
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/pll/dp_pll.h
> @@ -0,0 +1,64 @@
> +/* Copyright (c) 2016-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.
> + *
> + */
> +
> +#ifndef __DP_PLL_H
> +#define __DP_PLL_H
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/platform_device.h>
> +
> +#include "dpu_io_util.h"
> +#include "msm_drv.h"
> +
> +#define PLL_REG_W(base, offset, data)        \
> +                             writel_relaxed((data), (base) + (offset))
> +#define PLL_REG_R(base, offset)      readl_relaxed((base) + (offset))

These macros aren't really useful, tbh. It'd be better to have a function that
takes a msm_dp_pll, offset and data.

> +
> +enum msm_dp_pll_type {
> +     MSM_DP_PLL_10NM,
> +     MSM_DP_PLL_MAX
> +};
> +
> +struct msm_dp_pll {
> +     enum msm_dp_pll_type type;

Storing this doesn't seem to gain you anything. So move the dp_pll_type enum
into msm_dp_pll.c, turf the _MAX and make embed it in the .data element of the
of_device_id structs. Then you can use it when you're initializing and promptly
throw it away.

> +     struct clk_hw clk_hw;
> +     unsigned long   rate;           /* current vco rate */
> +     u64             min_rate;       /* min vco rate */
> +     u64             max_rate;       /* max vco rate */
> +     bool            pll_on;

The clk_hw/etc part of this patch should be reviewed by swboyd (cc'd). It's not
really in my wheelhouse, tbh.

> +     void            *priv;

Was going to comment on using an opaque pointer, but it not used anywhere \o/

Please remove

> +     /* Pll specific resources like GPIO, power supply, clocks, etc*/
> +     struct dss_module_power mp;
> +     int (*get_provider)(struct msm_dp_pll *pll,
> +                     struct clk **link_clk_provider,
> +                     struct clk **pixel_clk_provider);
> +};
> +
> +#define hw_clk_to_pll(x) container_of(x, struct msm_dp_pll, clk_hw)

Please make this a type-safe inline function.

> +
> +struct msm_dp_pll *msm_dp_pll_init(struct platform_device *pdev,
> +                     enum msm_dp_pll_type type, int id);

Remove this and make that func static

> +
> +int msm_dp_pll_util_parse_dt_clock(struct platform_device *pdev,
> +                                     struct msm_dp_pll *pll);
> +
> +#ifdef CONFIG_DRM_MSM_DP_10NM_PLL
> +struct msm_dp_pll *msm_dp_pll_10nm_init(struct platform_device *pdev, int 
> id);
> +#else
> +struct msm_dp_pll *msm_dp_pll_10nm_init(struct platform_device *pdev, int id)
> +{
> +     return ERR_PTR(-ENODEV);
> +}
> +#endif
> +#endif /* __DP_PLL_H */
> diff --git a/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c 
> b/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c
> new file mode 100644
> index 0000000..a180413
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c
> @@ -0,0 +1,401 @@
> +/* Copyright (c) 2016-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.
> + *
> + */
> +
> +/*
> + * Display Port PLL driver block diagram for branch clocks
> + *
> + *           +------------------------------+
> + *           |         DP_VCO_CLK           |
> + *           |                              |
> + *           |    +-------------------+     |
> + *           |    |   (DP PLL/VCO)    |     |
> + *           |    +---------+---------+     |
> + *           |              v               |
> + *           |   +----------+-----------+   |
> + *           |   | hsclk_divsel_clk_src |   |
> + *           |   +----------+-----------+   |
> + *           +------------------------------+
> + *                           |
> + *    +------------<---------v------------>----------+
> + *    |                                              |
> + * +-----v------------+                                 |
> + * | dp_link_clk_src  |                                 |
> + * |    divsel_ten    |                                 |
> + * +---------+--------+                                 |
> + *   |                                               |
> + *   |                                               |
> + *   v                                               v
> + * Input to DISPCC block                                |
> + * for link clk, crypto clk                             |
> + * and interface clock                                  |
> + *                                                   |
> + *                                                   |
> + *   +--------<------------+-----------------+---<---+
> + *   |                     |                 |
> + * +-------v------+  +--------v-----+  +--------v------+
> + * | vco_divided  |  | vco_divided  |  | vco_divided   |
> + * |    _clk_src  |  |    _clk_src  |  |    _clk_src   |
> + * |              |  |              |  |               |
> + * |divsel_six    |  |  divsel_two  |  |  divsel_four  |
> + * +-------+------+  +-----+--------+  +--------+------+
> + *         |            |                    |
> + *   v------->----------v-------------<------v
> + *                         |
> + *           +----------+---------+
> + *           |   vco_divided_clk  |
> + *           |       _src_mux     |
> + *           +---------+----------+
> + *                        |
> + *                        v
> + *              Input to DISPCC block
> + *              for DP pixel clock

Some of your vertical lines seem misaligned here, can you please fix this up?

> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/err.h>
> +#include <linux/delay.h>
> +#include <linux/regmap.h>
> +#include <linux/clk.h>
> +
> +#include "dp_pll_10nm.h"
> +
> +#define NUM_PROVIDED_CLKS            2
> +
> +#define DP_LINK_CLK_SRC                      0
> +#define DP_PIXEL_CLK_SRC             1

Instead of using an array to store the clocks in one place, why not just store a
pointer for each clk? Then you can get rid of these and just use the clk
directly.

> +
> +static struct dp_pll_10nm *dp_pdb;

This isn't used (nor should it be).

> +
> +/* Op structures */
> +static const struct clk_ops dp_10nm_vco_clk_ops = {
> +     .recalc_rate = dp_vco_recalc_rate_10nm,
> +     .set_rate = dp_vco_set_rate_10nm,
> +     .round_rate = dp_vco_round_rate_10nm,
> +     .prepare = dp_vco_prepare_10nm,
> +     .unprepare = dp_vco_unprepare_10nm,
> +};
> +
> +struct dp_pll_10nm_pclksel {
> +     struct clk_hw hw;
> +
> +     /* divider params */
> +     u8 shift;
> +     u8 width;
> +     u8 flags; /* same flags as used by clk_divider struct */
> +
> +     struct dp_pll_10nm *pll;
> +};
> +#define to_pll_10nm_pclksel(_hw) container_of(_hw, struct 
> dp_pll_10nm_pclksel, hw)

typesafe static inline please. Everywhere you use pclksel, you just grab ->pll
from the result and never use pclksel again. So make the function return
dp_pll_10nm * directly and save yourself the local var.

> +
> +static int dp_mux_set_parent_10nm(struct clk_hw *hw, u8 val)
> +{
> +     struct dp_pll_10nm_pclksel *pclksel = to_pll_10nm_pclksel(hw);
> +     struct dp_pll_10nm *dp_res = pclksel->pll;
> +     u32 auxclk_div;
> +
> +     auxclk_div = PLL_REG_R(dp_res->phy_base, DP_PHY_VCO_DIV);
> +     auxclk_div &= ~0x03;    /* bits 0 to 1 */

This comment isn't really useful :) Could you please #define all of the
hardcoded values in the patch?

> +
> +     if (val == 0) /* mux parent index = 0 */
> +             auxclk_div |= 1;
> +     else if (val == 1) /* mux parent index = 1 */
> +             auxclk_div |= 2;
> +     else if (val == 2) /* mux parent index = 2 */
> +             auxclk_div |= 0;
> +
> +     PLL_REG_W(dp_res->phy_base,
> +                     DP_PHY_VCO_DIV, auxclk_div);
> +     /* Make sure the PHY registers writes are done */
> +     wmb();

Same comments about needing wmb to work around using _relaxed

> +     pr_debug("%s: mux=%d auxclk_div=%x\n", __func__, val, auxclk_div);
> +
> +     return 0;
> +}
> +
> +static u8 dp_mux_get_parent_10nm(struct clk_hw *hw)
> +{
> +     u32 auxclk_div = 0;
> +     struct dp_pll_10nm_pclksel *pclksel = to_pll_10nm_pclksel(hw);
> +     struct dp_pll_10nm *dp_res = pclksel->pll;
> +     u8 val = 0;
> +
> +     pr_err("clk_hw->init->name = %s\n", hw->init->name);
> +     auxclk_div = PLL_REG_R(dp_res->phy_base, DP_PHY_VCO_DIV);
> +     auxclk_div &= 0x03;
> +
> +     if (auxclk_div == 1) /* Default divider */
> +             val = 0;
> +     else if (auxclk_div == 2)
> +             val = 1;
> +     else if (auxclk_div == 0)
> +             val = 2;
> +
> +     pr_debug("%s: auxclk_div=%d, val=%d\n", __func__, auxclk_div, val);
> +
> +     return val;
> +}
> +
> +static int clk_mux_determine_rate(struct clk_hw *hw,
> +                                  struct clk_rate_request *req)
> +{
> +     int ret = 0;

no need to init this to 0

> +
> +     ret = __clk_mux_determine_rate_closest(hw, req);
> +     if (ret)
> +             return ret;
> +
> +     /* Set the new parent of mux if there is a new valid parent */
> +     if (hw->clk && req->best_parent_hw->clk)
> +             clk_set_parent(hw->clk, req->best_parent_hw->clk);

What about the return value? All other clk_set_parent calls in this patch are
also unchecked, so please add those as well.

> +
> +     return 0;
> +}
> +
> +static unsigned long mux_recalc_rate(struct clk_hw *hw,
> +                                     unsigned long parent_rate)
> +{
> +     struct clk *div_clk = NULL, *vco_clk = NULL;
> +     struct msm_dp_pll *vco = NULL;
> +
> +     div_clk = clk_get_parent(hw->clk);
> +     if (!div_clk)

According to the header documentation, clk_get_parent can return ERR_PTR as
well. Same comment applies to other callsites.

> +             return 0;
> +
> +     vco_clk = clk_get_parent(div_clk);
> +     if (!vco_clk)
> +             return 0;
> +
> +     vco = hw_clk_to_pll(__clk_get_hw(vco_clk));
> +     if (!vco)
> +             return 0;
> +
> +     if (vco->rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000)
> +             return (vco->rate / 6);

Unnecessary () here and below

> +     else if (vco->rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000)
> +             return (vco->rate / 4);
> +     else
> +             return (vco->rate / 2);
> +}
> +
> +static int dp_pll_10nm_get_provider(struct msm_dp_pll *pll,
> +                                  struct clk **link_clk_provider,
> +                                  struct clk **pixel_clk_provider)
> +{
> +     struct dp_pll_10nm *pll_10nm = to_dp_pll_10nm(pll);
> +     struct clk_hw_onecell_data *hw_data = pll_10nm->hw_data;
> +
> +     if (link_clk_provider)
> +             *link_clk_provider = hw_data->hws[DP_LINK_CLK_SRC]->clk;
> +     if (pixel_clk_provider)
> +             *pixel_clk_provider = hw_data->hws[DP_PIXEL_CLK_SRC]->clk;
> +
> +     return 0;

Why have a return value when the only place always returns 0? Make it void and
simplify error checking at the callsite.

> +}
> +
> +static const struct clk_ops dp_10nm_pclksel_clk_ops = {
> +     .get_parent = dp_mux_get_parent_10nm,
> +     .set_parent = dp_mux_set_parent_10nm,
> +     .recalc_rate = mux_recalc_rate,
> +     .determine_rate = clk_mux_determine_rate,
> +};
> +
> +static struct clk_hw *dp_pll_10nm_pixel_clk_sel(struct dp_pll_10nm *pll_10nm)
> +{
> +     struct device *dev = &pll_10nm->pdev->dev;
> +     struct dp_pll_10nm_pclksel *pll_pclksel;
> +     struct clk_init_data pclksel_init = {
> +             .parent_names = (const char *[]){
> +                             "dp_vco_divsel_two_clk_src",
> +                             "dp_vco_divsel_four_clk_src",
> +                             "dp_vco_divsel_six_clk_src" },
> +             .num_parents = 3,
> +             .name = "dp_vco_divided_clk_src_mux",
> +             .flags = CLK_IGNORE_UNUSED,
> +             .ops = &dp_10nm_pclksel_clk_ops,
> +     };
> +     int ret;
> +
> +     pll_pclksel = devm_kzalloc(dev, sizeof(*pll_pclksel), GFP_KERNEL);
> +     if (!pll_pclksel)
> +             return ERR_PTR(-ENOMEM);
> +
> +     pll_pclksel->pll = pll_10nm;
> +     pll_pclksel->shift = 0;
> +     pll_pclksel->width = 4;
> +     pll_pclksel->flags = CLK_DIVIDER_ONE_BASED;
> +     pll_pclksel->hw.init = &pclksel_init;

pclksel_init goes out of scope at the end of the function, yet it is persisted
in pll_pclksel. That should be fixed.

> +
> +     ret = clk_hw_register(dev, &pll_pclksel->hw);
> +     if (ret)
> +             return ERR_PTR(ret);
> +
> +     return &pll_pclksel->hw;
> +}
> +
> +static int dp_pll_10nm_register(struct dp_pll_10nm *pll_10nm)
> +{
> +     char clk_name[32], parent[32], vco_name[32];
> +     struct clk_init_data vco_init = {
> +             .parent_names = (const char *[]){ "bi_tcxo" },
> +             .num_parents = 1,
> +             .name = vco_name,
> +             .flags = CLK_IGNORE_UNUSED,
> +             .ops = &dp_10nm_vco_clk_ops,
> +     };
> +     struct device *dev = &pll_10nm->pdev->dev;
> +     struct clk_hw **hws = pll_10nm->hws;
> +     struct clk_hw_onecell_data *hw_data;
> +     struct clk_hw *hw;
> +     int num = 0;
> +     int ret;
> +
> +     DBG("DP->id = %d", pll_10nm->id);
> +
> +     hw_data = devm_kzalloc(dev, sizeof(*hw_data) +
> +                            NUM_PROVIDED_CLKS * sizeof(struct clk_hw *),
> +                            GFP_KERNEL);
> +     if (!hw_data)
> +             return -ENOMEM;
> +
> +     snprintf(vco_name, 32, "dp_vco_clk");
> +     pll_10nm->base.clk_hw.init = &vco_init;

Same comment about scope here

> +     ret = clk_hw_register(dev, &pll_10nm->base.clk_hw);
> +     if (ret)
> +             return ret;
> +     hws[num++] = &pll_10nm->base.clk_hw;
> +
> +     snprintf(clk_name, 32, "dp_link_clk_divsel_ten");
> +     snprintf(parent, 32, "dp_vco_clk");
> +     hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
> +                                       CLK_SET_RATE_PARENT, 1, 10);
> +     if (IS_ERR(hw))
> +             return PTR_ERR(hw);
> +     hws[num++] = hw;
> +     hw_data->hws[DP_LINK_CLK_SRC] = hw;
> +
> +     snprintf(clk_name, 32, "dp_vco_divsel_two_clk_src");
> +     snprintf(parent, 32, "dp_vco_clk");
> +     hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
> +                                       0, 1, 2);
> +     if (IS_ERR(hw))
> +             return PTR_ERR(hw);
> +     hws[num++] = hw;
> +
> +     snprintf(clk_name, 32, "dp_vco_divsel_four_clk_src");
> +     snprintf(parent, 32, "dp_vco_clk");
> +     hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
> +                                       0, 1, 4);
> +     if (IS_ERR(hw))
> +             return PTR_ERR(hw);
> +     hws[num++] = hw;
> +
> +     snprintf(clk_name, 32, "dp_vco_divsel_six_clk_src");
> +     snprintf(parent, 32, "dp_vco_clk");
> +     hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
> +                                       0, 1, 6);
> +     if (IS_ERR(hw))
> +             return PTR_ERR(hw);
> +     hws[num++] = hw;
> +
> +     hw = dp_pll_10nm_pixel_clk_sel(pll_10nm);
> +     if (IS_ERR(hw))
> +             return PTR_ERR(hw);
> +
> +     hws[num++] = hw;
> +     hw_data->hws[DP_PIXEL_CLK_SRC] = hw;

I'm going to leave this chunk for Stephen to comment on, but again I'd rather
not store these clocks as an array and do string lookups on them.

> +
> +     pll_10nm->num_hws = num;
> +
> +     hw_data->num = NUM_PROVIDED_CLKS;
> +     pll_10nm->hw_data = hw_data;
> +
> +     ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
> +                                  pll_10nm->hw_data);
> +     if (ret) {
> +             dev_err(dev, "failed to register clk provider: %d\n", ret);
> +             return ret;
> +     }
> +
> +     return ret;
> +}
> +
> +struct msm_dp_pll *msm_dp_pll_10nm_init(struct platform_device *pdev, int id)
> +{
> +     struct dp_pll_10nm *dp_10nm_pll;
> +     struct msm_dp_pll *pll;
> +     int ret;
> +
> +     if (!pdev)
> +             return ERR_PTR(-ENODEV);
> +
> +     dp_10nm_pll = devm_kzalloc(&pdev->dev, sizeof(*dp_10nm_pll), 
> GFP_KERNEL);
> +     if (!dp_10nm_pll)
> +             return ERR_PTR(-ENOMEM);
> +
> +     DBG("DP PLL%d", id);

Please remove (or convert to DRM_DEV_DEBUG)

> +
> +     dp_10nm_pll->pdev = pdev;
> +     dp_10nm_pll->id = id;
> +     dp_pdb = dp_10nm_pll;
> +
> +     dp_10nm_pll->pll_base = msm_ioremap(pdev, "pll_base", "DP_PLL");
> +     if (IS_ERR_OR_NULL(dp_10nm_pll->pll_base)) {
> +             dev_err(&pdev->dev, "failed to map CMN PLL base\n");

Print the error if pll_base is ERR_PTR, same for below.

> +             return ERR_PTR(-ENOMEM);

You should preserve the error if pll_base is an ERR_PTR, same for below.

> +     }
> +
> +     dp_10nm_pll->phy_base = msm_ioremap(pdev, "phy_base", "DP_PHY");
> +     if (IS_ERR_OR_NULL(dp_10nm_pll->phy_base)) {
> +             dev_err(&pdev->dev, "failed to map CMN PHY base\n");
> +             return ERR_PTR(-ENOMEM);
> +     }
> +
> +     dp_10nm_pll->ln_tx0_base = msm_ioremap(pdev, "ln_tx0_base", 
> "DP_LN_TX0");
> +     if (IS_ERR_OR_NULL(dp_10nm_pll->ln_tx0_base)) {
> +             dev_err(&pdev->dev, "failed to map CMN LN_TX0 base\n");
> +             return ERR_PTR(-ENOMEM);
> +     }
> +
> +     dp_10nm_pll->ln_tx1_base = msm_ioremap(pdev, "ln_tx1_base", 
> "DP_LN_TX1");
> +     if (IS_ERR_OR_NULL(dp_10nm_pll->ln_tx1_base)) {
> +             dev_err(&pdev->dev, "failed to map CMN LN_TX1 base\n");
> +             return ERR_PTR(-ENOMEM);
> +     }
> +
> +     ret = of_property_read_u32(pdev->dev.of_node, "cell-index",
> +                             &dp_10nm_pll->index);
> +     if (ret) {
> +             pr_err("Unable to get the cell-index ret=%d\n", ret);

If this is truly an error condition, we should return an error. If it's not,
downgrade this to info

> +             dp_10nm_pll->index = 0;
> +     }
> +
> +     ret = msm_dp_pll_util_parse_dt_clock(pdev, &dp_10nm_pll->base);
> +     if (ret) {
> +             pr_err("Unable to parse dt clocks ret=%d\n", ret);
> +             return ERR_PTR(ret);
> +     }
> +
> +     ret = dp_pll_10nm_register(dp_10nm_pll);
> +     if (ret) {
> +             dev_err(&pdev->dev, "failed to register PLL: %d\n", ret);
> +             return ERR_PTR(ret);
> +     }
> +
> +     pll = &dp_10nm_pll->base;
> +     pll->min_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
> +     pll->max_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;
> +     pll->get_provider = dp_pll_10nm_get_provider;
> +
> +     return pll;
> +}
> diff --git a/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h 
> b/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h
> new file mode 100644
> index 0000000..f966beb
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h
> @@ -0,0 +1,94 @@
> +/* Copyright (c) 2016-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.
> + *
> + */
> +
> +#ifndef __DP_PLL_10NM_H
> +#define __DP_PLL_10NM_H
> +
> +#include "dp_pll.h"
> +#include "dp_reg.h"
> +
> +#define DP_VCO_HSCLK_RATE_1620MHZDIV1000     1620000UL

Isn't MHZDIV1000 just KHZ? Same for below

> +#define DP_VCO_HSCLK_RATE_2700MHZDIV1000     2700000UL
> +#define DP_VCO_HSCLK_RATE_5400MHZDIV1000     5400000UL
> +#define DP_VCO_HSCLK_RATE_8100MHZDIV1000     8100000UL
> +
> +#define NUM_DP_CLOCKS_MAX                    6
> +
> +#define DP_PHY_PLL_POLL_SLEEP_US             500
> +#define DP_PHY_PLL_POLL_TIMEOUT_US           10000

These can go in the c file (and probably the others too)

> +
> +#define DP_VCO_RATE_8100MHZDIV1000           8100000UL
> +#define DP_VCO_RATE_9720MHZDIV1000           9720000UL
> +#define DP_VCO_RATE_10800MHZDIV1000          10800000UL
> +
> +struct dp_pll_10nm {
> +     struct msm_dp_pll base;
> +
> +     int id;
> +     struct platform_device *pdev;
> +
> +     void __iomem *pll_base;
> +     void __iomem *phy_base;
> +     void __iomem *ln_tx0_base;
> +     void __iomem *ln_tx1_base;
> +
> +     /* private clocks: */
> +     struct clk_hw *hws[NUM_DP_CLOCKS_MAX];
> +     u32 num_hws;
> +
> +     /* clock-provider: */
> +     struct clk_hw_onecell_data *hw_data;
> +
> +     /* lane and orientation settings */
> +     u8 lane_cnt;
> +     u8 orientation;
> +
> +     /* COM PHY settings */
> +     u32 hsclk_sel;
> +     u32 dec_start_mode0;
> +     u32 div_frac_start1_mode0;
> +     u32 div_frac_start2_mode0;
> +     u32 div_frac_start3_mode0;
> +     u32 integloop_gain0_mode0;
> +     u32 integloop_gain1_mode0;
> +     u32 vco_tune_map;
> +     u32 lock_cmp1_mode0;
> +     u32 lock_cmp2_mode0;
> +     u32 lock_cmp3_mode0;
> +     u32 lock_cmp_en;
> +
> +     /* PHY vco divider */
> +     u32 phy_vco_div;
> +     /*
> +      * Certain pll's needs to update the same vco rate after resume in
> +      * suspend/resume scenario. Cached the vco rate for such plls.
> +      */
> +     unsigned long   vco_cached_rate;
> +     u32             cached_cfg0;
> +     u32             cached_cfg1;
> +     u32             cached_outdiv;
> +
> +     uint32_t index;
> +};
> +
> +#define to_dp_pll_10nm(x)    container_of(x, struct dp_pll_10nm, base)

typesafe inline pls

> +
> +int dp_vco_set_rate_10nm(struct clk_hw *hw, unsigned long rate,
> +                             unsigned long parent_rate);
> +unsigned long dp_vco_recalc_rate_10nm(struct clk_hw *hw,
> +                             unsigned long parent_rate);
> +long dp_vco_round_rate_10nm(struct clk_hw *hw, unsigned long rate,
> +                             unsigned long *parent_rate);
> +int dp_vco_prepare_10nm(struct clk_hw *hw);
> +void dp_vco_unprepare_10nm(struct clk_hw *hw);
> +#endif /* __DP_PLL_10NM_H */
> diff --git a/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c 
> b/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c
> new file mode 100644
> index 0000000..9eb6881
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c
> @@ -0,0 +1,531 @@
> +/* Copyright (c) 2016-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.
> + *
> + */
> +
> +#define pr_fmt(fmt)  "%s: " fmt, __func__
> +
> +#include <linux/kernel.h>
> +#include <linux/err.h>
> +#include <linux/iopoll.h>
> +#include <linux/delay.h>
> +
> +#include "dp_pll.h"
> +#include "dp_pll_10nm.h"
> +#include "dp_extcon.h"
> +
> +static int dp_vco_pll_init_db_10nm(struct msm_dp_pll *pll,
> +             unsigned long rate)
> +{
> +     struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +     u32 spare_value = 0;
> +
> +     spare_value = PLL_REG_R(dp_res->phy_base, DP_PHY_SPARE0);
> +     dp_res->lane_cnt = spare_value & 0x0F;
> +     dp_res->orientation = (spare_value & 0xF0) >> 4;
> +
> +     pr_debug("%s: spare_value=0x%x, ln_cnt=0x%x, orientation=0x%x\n",
> +                     __func__, spare_value, dp_res->lane_cnt, 
> dp_res->orientation);
> +
> +     switch (rate) {
> +     case DP_VCO_HSCLK_RATE_1620MHZDIV1000:
> +             pr_debug("%s: VCO rate: %ld\n", __func__,
> +                             DP_VCO_RATE_9720MHZDIV1000);
> +             dp_res->hsclk_sel = 0x0c;
> +             dp_res->dec_start_mode0 = 0x69;
> +             dp_res->div_frac_start1_mode0 = 0x00;
> +             dp_res->div_frac_start2_mode0 = 0x80;
> +             dp_res->div_frac_start3_mode0 = 0x07;
> +             dp_res->integloop_gain0_mode0 = 0x3f;
> +             dp_res->integloop_gain1_mode0 = 0x00;
> +             dp_res->vco_tune_map = 0x00;
> +             dp_res->lock_cmp1_mode0 = 0x6f;
> +             dp_res->lock_cmp2_mode0 = 0x08;
> +             dp_res->lock_cmp3_mode0 = 0x00;
> +             dp_res->phy_vco_div = 0x1;
> +             dp_res->lock_cmp_en = 0x00;
> +             break;
> +     case DP_VCO_HSCLK_RATE_2700MHZDIV1000:
> +             pr_debug("%s: VCO rate: %ld\n", __func__,
> +                             DP_VCO_RATE_10800MHZDIV1000);
> +             dp_res->hsclk_sel = 0x04;
> +             dp_res->dec_start_mode0 = 0x69;
> +             dp_res->div_frac_start1_mode0 = 0x00;
> +             dp_res->div_frac_start2_mode0 = 0x80;
> +             dp_res->div_frac_start3_mode0 = 0x07;
> +             dp_res->integloop_gain0_mode0 = 0x3f;
> +             dp_res->integloop_gain1_mode0 = 0x00;
> +             dp_res->vco_tune_map = 0x00;
> +             dp_res->lock_cmp1_mode0 = 0x0f;
> +             dp_res->lock_cmp2_mode0 = 0x0e;
> +             dp_res->lock_cmp3_mode0 = 0x00;
> +             dp_res->phy_vco_div = 0x1;
> +             dp_res->lock_cmp_en = 0x00;
> +             break;
> +     case DP_VCO_HSCLK_RATE_5400MHZDIV1000:
> +             pr_debug("%s: VCO rate: %ld\n", __func__,
> +                             DP_VCO_RATE_10800MHZDIV1000);
> +             dp_res->hsclk_sel = 0x00;
> +             dp_res->dec_start_mode0 = 0x8c;
> +             dp_res->div_frac_start1_mode0 = 0x00;
> +             dp_res->div_frac_start2_mode0 = 0x00;
> +             dp_res->div_frac_start3_mode0 = 0x0a;
> +             dp_res->integloop_gain0_mode0 = 0x3f;
> +             dp_res->integloop_gain1_mode0 = 0x00;
> +             dp_res->vco_tune_map = 0x00;
> +             dp_res->lock_cmp1_mode0 = 0x1f;
> +             dp_res->lock_cmp2_mode0 = 0x1c;
> +             dp_res->lock_cmp3_mode0 = 0x00;
> +             dp_res->phy_vco_div = 0x2;
> +             dp_res->lock_cmp_en = 0x00;
> +             break;
> +     case DP_VCO_HSCLK_RATE_8100MHZDIV1000:
> +             pr_debug("%s: VCO rate: %ld\n", __func__,
> +                             DP_VCO_RATE_8100MHZDIV1000);
> +             dp_res->hsclk_sel = 0x03;
> +             dp_res->dec_start_mode0 = 0x69;
> +             dp_res->div_frac_start1_mode0 = 0x00;
> +             dp_res->div_frac_start2_mode0 = 0x80;
> +             dp_res->div_frac_start3_mode0 = 0x07;
> +             dp_res->integloop_gain0_mode0 = 0x3f;
> +             dp_res->integloop_gain1_mode0 = 0x00;
> +             dp_res->vco_tune_map = 0x00;
> +             dp_res->lock_cmp1_mode0 = 0x2f;
> +             dp_res->lock_cmp2_mode0 = 0x2a;
> +             dp_res->lock_cmp3_mode0 = 0x00;
> +             dp_res->phy_vco_div = 0x0;
> +             dp_res->lock_cmp_en = 0x08;
> +             break;

Since this is all static, a static const lookup table would probably be more
appropriate.

> +     default:
> +             return -EINVAL;

Log this please

> +     }
> +     return 0;
> +}
> +
> +static int dp_config_vco_rate_10nm(struct msm_dp_pll *pll,
> +             unsigned long rate)
> +{
> +     u32 res = 0;
> +     struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +
> +     res = dp_vco_pll_init_db_10nm(pll, rate);
> +     if (res) {
> +             pr_err("VCO Init DB failed\n");
> +             return res;
> +     }
> +
> +     if (dp_res->lane_cnt != 4) {
> +             if (dp_res->orientation == ORIENTATION_CC2)
> +                     PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x6d);
> +             else
> +                     PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x75);
> +     } else {
> +             PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x7d);
> +     }
> +
> +     /* Make sure the PHY register writes are done */
> +     wmb();
> +
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_EN_SEL, 0x37);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x02);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_ENABLE1, 0x0e);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_SEL, 0x30);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_CMN_CONFIG, 0x02);
> +
> +     /* Different for each clock rates */
> +     PLL_REG_W(dp_res->pll_base,
> +             QSERDES_COM_HSCLK_SEL, dp_res->hsclk_sel);
> +     PLL_REG_W(dp_res->pll_base,
> +             QSERDES_COM_DEC_START_MODE0, dp_res->dec_start_mode0);
> +     PLL_REG_W(dp_res->pll_base,
> +             QSERDES_COM_DIV_FRAC_START1_MODE0, 
> dp_res->div_frac_start1_mode0);
> +     PLL_REG_W(dp_res->pll_base,
> +             QSERDES_COM_DIV_FRAC_START2_MODE0, 
> dp_res->div_frac_start2_mode0);
> +     PLL_REG_W(dp_res->pll_base,
> +             QSERDES_COM_DIV_FRAC_START3_MODE0, 
> dp_res->div_frac_start3_mode0);
> +     PLL_REG_W(dp_res->pll_base,
> +             QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 
> dp_res->integloop_gain0_mode0);
> +     PLL_REG_W(dp_res->pll_base,
> +             QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 
> dp_res->integloop_gain1_mode0);
> +     PLL_REG_W(dp_res->pll_base,
> +             QSERDES_COM_VCO_TUNE_MAP, dp_res->vco_tune_map);
> +     PLL_REG_W(dp_res->pll_base,
> +             QSERDES_COM_LOCK_CMP1_MODE0, dp_res->lock_cmp1_mode0);
> +     PLL_REG_W(dp_res->pll_base,
> +             QSERDES_COM_LOCK_CMP2_MODE0, dp_res->lock_cmp2_mode0);
> +     PLL_REG_W(dp_res->pll_base,
> +             QSERDES_COM_LOCK_CMP3_MODE0, dp_res->lock_cmp3_mode0);
> +     /* Make sure the PLL register writes are done */
> +     wmb();
> +
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_BG_TIMER, 0x0a);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORECLK_DIV_MODE0, 0x0a);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_VCO_TUNE_CTRL, 0x00);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3f);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORE_CLK_EN, 0x1f);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_IVCO, 0x07);
> +     PLL_REG_W(dp_res->pll_base,
> +             QSERDES_COM_LOCK_CMP_EN, dp_res->lock_cmp_en);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_CCTRL_MODE0, 0x36);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_CP_CTRL_MODE0, 0x06);
> +     /* Make sure the PHY register writes are done */
> +     wmb();
> +
> +     if (dp_res->orientation == ORIENTATION_CC2)
> +             PLL_REG_W(dp_res->phy_base, DP_PHY_MODE, 0x4c);
> +     else
> +             PLL_REG_W(dp_res->phy_base, DP_PHY_MODE, 0x5c);
> +     /* Make sure the PLL register writes are done */
> +     wmb();
> +
> +     /* TX Lane configuration */
> +     PLL_REG_W(dp_res->phy_base,
> +                     DP_PHY_TX0_TX1_LANE_CTL, 0x05);
> +     PLL_REG_W(dp_res->phy_base,
> +                     DP_PHY_TX2_TX3_LANE_CTL, 0x05);
> +
> +     /* TX-0 register configuration */
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_TRANSCEIVER_BIAS_EN, 0x1a);
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_VMODE_CTRL1, 0x40);
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_INTERFACE_SELECT, 0x3d);
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_CLKBUF_ENABLE, 0x0f);
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_RESET_TSYNC_EN, 0x03);
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_TRAN_DRVR_EMP_EN, 0x03);
> +     PLL_REG_W(dp_res->ln_tx0_base,
> +             TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_INTERFACE_MODE, 0x00);
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_BAND, 0x4);
> +
> +     /* TX-1 register configuration */
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_TRANSCEIVER_BIAS_EN, 0x1a);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_VMODE_CTRL1, 0x40);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_INTERFACE_SELECT, 0x3d);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_CLKBUF_ENABLE, 0x0f);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_RESET_TSYNC_EN, 0x03);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_TRAN_DRVR_EMP_EN, 0x03);
> +     PLL_REG_W(dp_res->ln_tx1_base,
> +             TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_INTERFACE_MODE, 0x00);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_BAND, 0x4);
> +     /* Make sure the PHY register writes are done */
> +     wmb();
> +
> +     /* dependent on the vco frequency */
> +     PLL_REG_W(dp_res->phy_base, DP_PHY_VCO_DIV, dp_res->phy_vco_div);
> +
> +     return res;
> +}
> +
> +static bool dp_10nm_pll_lock_status(struct dp_pll_10nm *dp_res)
> +{
> +     u32 status;
> +     bool pll_locked;
> +
> +     /* poll for PLL lock status */
> +     if (readl_poll_timeout_atomic((dp_res->pll_base +
> +                     QSERDES_COM_C_READY_STATUS),
> +                     status,
> +                     ((status & BIT(0)) > 0),
> +                     DP_PHY_PLL_POLL_SLEEP_US,
> +                     DP_PHY_PLL_POLL_TIMEOUT_US)) {
> +             pr_err("%s: C_READY status is not high. Status=%x\n",
> +                             __func__, status);
> +             pll_locked = false;
> +     } else {
> +             pll_locked = true;
> +     }
> +
> +     return pll_locked;
> +}
> +
> +static bool dp_10nm_phy_rdy_status(struct dp_pll_10nm *dp_res)
> +{
> +     u32 status;
> +     bool phy_ready = true;
> +
> +     /* poll for PHY ready status */
> +     if (readl_poll_timeout_atomic((dp_res->phy_base +
> +                     DP_PHY_STATUS),
> +                     status,
> +                     ((status & (BIT(1))) > 0),
> +                     DP_PHY_PLL_POLL_SLEEP_US,
> +                     DP_PHY_PLL_POLL_TIMEOUT_US)) {
> +             pr_err("%s: Phy_ready is not high. Status=%x\n",
> +                             __func__, status);
> +             phy_ready = false;
> +     }
> +
> +     return phy_ready;
> +}
> +
> +static int dp_pll_enable_10nm(struct clk_hw *hw)
> +{
> +     int rc = 0;
> +     struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +     struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +     u32 bias_en, drvr_en;
> +
> +     PLL_REG_W(dp_res->phy_base, DP_PHY_AUX_CFG2, 0x04);
> +     PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x01);
> +     PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x05);
> +     PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x01);
> +     PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x09);
> +     wmb(); /* Make sure the PHY register writes are done */
> +
> +     PLL_REG_W(dp_res->pll_base, QSERDES_COM_RESETSM_CNTRL, 0x20);
> +     wmb();  /* Make sure the PLL register writes are done */
> +
> +     if (!dp_10nm_pll_lock_status(dp_res)) {
> +             rc = -EINVAL;
> +             goto lock_err;
> +     }
> +
> +     PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x19);
> +     /* Make sure the PHY register writes are done */
> +     wmb();
> +     /* poll for PHY ready status */
> +     if (!dp_10nm_phy_rdy_status(dp_res)) {
> +             rc = -EINVAL;
> +             goto lock_err;
> +     }
> +
> +     pr_debug("%s: PLL is locked\n", __func__);
> +
> +     if (dp_res->lane_cnt == 1) {
> +             bias_en = 0x3e;
> +             drvr_en = 0x13;
> +     } else {
> +             bias_en = 0x3f;
> +             drvr_en = 0x10;
> +     }
> +
> +     if (dp_res->lane_cnt != 4) {
> +             if (dp_res->orientation == ORIENTATION_CC1) {
> +                     PLL_REG_W(dp_res->ln_tx1_base,
> +                             TXn_HIGHZ_DRVR_EN, drvr_en);
> +                     PLL_REG_W(dp_res->ln_tx1_base,
> +                             TXn_TRANSCEIVER_BIAS_EN, bias_en);
> +             } else {
> +                     PLL_REG_W(dp_res->ln_tx0_base,
> +                             TXn_HIGHZ_DRVR_EN, drvr_en);
> +                     PLL_REG_W(dp_res->ln_tx0_base,
> +                             TXn_TRANSCEIVER_BIAS_EN, bias_en);
> +             }
> +     } else {
> +             PLL_REG_W(dp_res->ln_tx0_base, TXn_HIGHZ_DRVR_EN, drvr_en);
> +             PLL_REG_W(dp_res->ln_tx0_base,
> +                     TXn_TRANSCEIVER_BIAS_EN, bias_en);
> +             PLL_REG_W(dp_res->ln_tx1_base, TXn_HIGHZ_DRVR_EN, drvr_en);
> +             PLL_REG_W(dp_res->ln_tx1_base,
> +                     TXn_TRANSCEIVER_BIAS_EN, bias_en);
> +     }

I think you could probably simplify this code block with a bit of effort,
especially the top condition.

> +
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_POL_INV, 0x0a);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_POL_INV, 0x0a);
> +     PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x18);
> +     udelay(2000);

why udelay?

> +
> +     PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x19);
> +
> +     /*
> +      * Make sure all the register writes are completed before
> +      * doing any other operation
> +      */
> +     wmb();
> +
> +     /* poll for PHY ready status */
> +     if (!dp_10nm_phy_rdy_status(dp_res)) {
> +             rc = -EINVAL;
> +             goto lock_err;
> +     }
> +
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_DRV_LVL, 0x38);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_DRV_LVL, 0x38);
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_EMP_POST1_LVL, 0x20);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_EMP_POST1_LVL, 0x20);
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_RES_CODE_LANE_OFFSET_TX, 0x06);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_RES_CODE_LANE_OFFSET_TX, 0x06);
> +     PLL_REG_W(dp_res->ln_tx0_base, TXn_RES_CODE_LANE_OFFSET_RX, 0x07);
> +     PLL_REG_W(dp_res->ln_tx1_base, TXn_RES_CODE_LANE_OFFSET_RX, 0x07);
> +     /* Make sure the PHY register writes are done */
> +     wmb();
> +
> +lock_err:
> +     return rc;

Remove lock_err and return directly above.

> +}
> +
> +static int dp_pll_disable_10nm(struct clk_hw *hw)
> +{
> +     int rc = 0;
> +     struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +     struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +
> +     /* Assert DP PHY power down */
> +     PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x2);
> +     /*
> +      * Make sure all the register writes to disable PLL are
> +      * completed before doing any other operation
> +      */
> +     wmb();
> +
> +     return rc;
> +}
> +
> +
> +int dp_vco_prepare_10nm(struct clk_hw *hw)
> +{
> +     int rc = 0;
> +     struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +     struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +
> +     pr_debug("rate=%ld\n", pll->rate);
> +     if ((dp_res->vco_cached_rate != 0)
> +             && (dp_res->vco_cached_rate == pll->rate)) {


        if (dp_res->vco_cached_rate && dp_res->vco_cached_rate == pll->rate) {


> +             rc = pll->clk_hw.init->ops->set_rate(hw,
> +                     dp_res->vco_cached_rate, dp_res->vco_cached_rate);
> +             if (rc) {
> +                     pr_err("index=%d vco_set_rate failed. rc=%d\n",
> +                             rc, dp_res->index);
> +                     goto error;
> +             }
> +     }
> +
> +     rc = dp_pll_enable_10nm(hw);
> +     if (rc) {
> +             pr_err("ndx=%d failed to enable dp pll\n",
> +                                     dp_res->index);
> +             goto error;
> +     }
> +
> +     pll->pll_on = true;

Do you need locking around the prepare/unprepare functions?


> +error:
> +     return rc;

Same comment as always

> +}
> +
> +void dp_vco_unprepare_10nm(struct clk_hw *hw)
> +{
> +     struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +     struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +
> +     if (!dp_res) {
> +             pr_err("Invalid input parameter\n");
> +             return;
> +     }
> +
> +     if (!pll->pll_on) {
> +             pr_err("pll resource can't be enabled\n");
> +             return;
> +     }
> +     dp_res->vco_cached_rate = pll->rate;
> +     dp_pll_disable_10nm(hw);
> +
> +     //dp_res->handoff_resources = false;

??

> +     pll->pll_on = false;
> +}
> +
> +int dp_vco_set_rate_10nm(struct clk_hw *hw, unsigned long rate,
> +                                     unsigned long parent_rate)
> +{
> +     struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +     int rc;
> +
> +     pr_debug("DP lane CLK rate=%ld\n", rate);
> +
> +     rc = dp_config_vco_rate_10nm(pll, rate);
> +     if (rc)
> +             pr_err("%s: Failed to set clk rate\n", __func__);

Should you return early here?

> +
> +     pll->rate = rate;
> +
> +     return 0;
> +}
> +
> +unsigned long dp_vco_recalc_rate_10nm(struct clk_hw *hw,
> +                                     unsigned long parent_rate)
> +{
> +     struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +     struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +     u32 div, hsclk_div, link_clk_div = 0;
> +     u64 vco_rate;
> +
> +     div = PLL_REG_R(dp_res->pll_base, QSERDES_COM_HSCLK_SEL);
> +     div &= 0x0f;
> +
> +     if (div == 12)
> +             hsclk_div = 6; /* Default */
> +     else if (div == 4)
> +             hsclk_div = 4;
> +     else if (div == 0)
> +             hsclk_div = 2;
> +     else if (div == 3)
> +             hsclk_div = 1;
> +     else {
> +             pr_debug("unknown divider. forcing to default\n");
> +             hsclk_div = 5;
> +     }
> +
> +     div = PLL_REG_R(dp_res->phy_base, DP_PHY_AUX_CFG2);
> +     div >>= 2;
> +
> +     if ((div & 0x3) == 0)
> +             link_clk_div = 5;
> +     else if ((div & 0x3) == 1)
> +             link_clk_div = 10;
> +     else if ((div & 0x3) == 2)
> +             link_clk_div = 20;
> +     else
> +             pr_err("%s: unsupported div. Phy_mode: %d\n", __func__, div);
> +
> +     if (link_clk_div == 20) {
> +             vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
> +     } else {
> +             if (hsclk_div == 6)
> +                     vco_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
> +             else if (hsclk_div == 4)
> +                     vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
> +             else if (hsclk_div == 2)
> +                     vco_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
> +             else
> +                     vco_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;
> +     }
> +
> +     pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
> +
> +     dp_res->vco_cached_rate = pll->rate = vco_rate;
> +     return (unsigned long)vco_rate;

Hopefully this function will become easier to parse with #define'd values

> +}
> +
> +long dp_vco_round_rate_10nm(struct clk_hw *hw, unsigned long rate,
> +                     unsigned long *parent_rate)
> +{
> +     unsigned long rrate = rate;
> +     struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +
> +     if (rate <= pll->min_rate)
> +             rrate = pll->min_rate;
> +     else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
> +             rrate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
> +     else if (rate <= DP_VCO_HSCLK_RATE_5400MHZDIV1000)
> +             rrate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
> +     else
> +             rrate = pll->max_rate;

You're assuming min_rate is < 2.7MHz and max_rate > 5.4 MHz. This is true in the
current code, but could change in the future. Fortunatley min/max_rate are only
used here. So delete them from the struct and use the DP_VCO_HSCLK_RATE_* values
directly here.

> +
> +     pr_debug("%s: rrate=%ld\n", __func__, rrate);
> +
> +     *parent_rate = rrate;
> +     return rrate;
> +}
> +
> -- 
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to