linux/drivers/gpu/drm/msm/hdmi/hdmi_phy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2016, The Linux Foundation. All rights reserved.
   4 */
   5
   6#include <linux/of_device.h>
   7
   8#include "hdmi.h"
   9
  10static int msm_hdmi_phy_resource_init(struct hdmi_phy *phy)
  11{
  12        struct hdmi_phy_cfg *cfg = phy->cfg;
  13        struct device *dev = &phy->pdev->dev;
  14        int i, ret;
  15
  16        phy->regs = devm_kcalloc(dev, cfg->num_regs, sizeof(phy->regs[0]),
  17                                 GFP_KERNEL);
  18        if (!phy->regs)
  19                return -ENOMEM;
  20
  21        phy->clks = devm_kcalloc(dev, cfg->num_clks, sizeof(phy->clks[0]),
  22                                 GFP_KERNEL);
  23        if (!phy->clks)
  24                return -ENOMEM;
  25
  26        for (i = 0; i < cfg->num_regs; i++) {
  27                struct regulator *reg;
  28
  29                reg = devm_regulator_get(dev, cfg->reg_names[i]);
  30                if (IS_ERR(reg)) {
  31                        ret = PTR_ERR(reg);
  32                        if (ret != -EPROBE_DEFER) {
  33                                DRM_DEV_ERROR(dev,
  34                                              "failed to get phy regulator: %s (%d)\n",
  35                                              cfg->reg_names[i], ret);
  36                        }
  37
  38                        return ret;
  39                }
  40
  41                phy->regs[i] = reg;
  42        }
  43
  44        for (i = 0; i < cfg->num_clks; i++) {
  45                struct clk *clk;
  46
  47                clk = msm_clk_get(phy->pdev, cfg->clk_names[i]);
  48                if (IS_ERR(clk)) {
  49                        ret = PTR_ERR(clk);
  50                        DRM_DEV_ERROR(dev, "failed to get phy clock: %s (%d)\n",
  51                                cfg->clk_names[i], ret);
  52                        return ret;
  53                }
  54
  55                phy->clks[i] = clk;
  56        }
  57
  58        return 0;
  59}
  60
  61int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy)
  62{
  63        struct hdmi_phy_cfg *cfg = phy->cfg;
  64        struct device *dev = &phy->pdev->dev;
  65        int i, ret = 0;
  66
  67        pm_runtime_get_sync(dev);
  68
  69        for (i = 0; i < cfg->num_regs; i++) {
  70                ret = regulator_enable(phy->regs[i]);
  71                if (ret)
  72                        DRM_DEV_ERROR(dev, "failed to enable regulator: %s (%d)\n",
  73                                cfg->reg_names[i], ret);
  74        }
  75
  76        for (i = 0; i < cfg->num_clks; i++) {
  77                ret = clk_prepare_enable(phy->clks[i]);
  78                if (ret)
  79                        DRM_DEV_ERROR(dev, "failed to enable clock: %s (%d)\n",
  80                                cfg->clk_names[i], ret);
  81        }
  82
  83        return ret;
  84}
  85
  86void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy)
  87{
  88        struct hdmi_phy_cfg *cfg = phy->cfg;
  89        struct device *dev = &phy->pdev->dev;
  90        int i;
  91
  92        for (i = cfg->num_clks - 1; i >= 0; i--)
  93                clk_disable_unprepare(phy->clks[i]);
  94
  95        for (i = cfg->num_regs - 1; i >= 0; i--)
  96                regulator_disable(phy->regs[i]);
  97
  98        pm_runtime_put_sync(dev);
  99}
 100
 101void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock)
 102{
 103        if (!phy || !phy->cfg->powerup)
 104                return;
 105
 106        phy->cfg->powerup(phy, pixclock);
 107}
 108
 109void msm_hdmi_phy_powerdown(struct hdmi_phy *phy)
 110{
 111        if (!phy || !phy->cfg->powerdown)
 112                return;
 113
 114        phy->cfg->powerdown(phy);
 115}
 116
 117static int msm_hdmi_phy_pll_init(struct platform_device *pdev,
 118                             enum hdmi_phy_type type)
 119{
 120        int ret;
 121
 122        switch (type) {
 123        case MSM_HDMI_PHY_8960:
 124                ret = msm_hdmi_pll_8960_init(pdev);
 125                break;
 126        case MSM_HDMI_PHY_8996:
 127                ret = msm_hdmi_pll_8996_init(pdev);
 128                break;
 129        /*
 130         * we don't have PLL support for these, don't report an error for now
 131         */
 132        case MSM_HDMI_PHY_8x60:
 133        case MSM_HDMI_PHY_8x74:
 134        default:
 135                ret = 0;
 136                break;
 137        }
 138
 139        return ret;
 140}
 141
 142static int msm_hdmi_phy_probe(struct platform_device *pdev)
 143{
 144        struct device *dev = &pdev->dev;
 145        struct hdmi_phy *phy;
 146        int ret;
 147
 148        phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
 149        if (!phy)
 150                return -ENODEV;
 151
 152        phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev);
 153        if (!phy->cfg)
 154                return -ENODEV;
 155
 156        phy->mmio = msm_ioremap(pdev, "hdmi_phy", "HDMI_PHY");
 157        if (IS_ERR(phy->mmio)) {
 158                DRM_DEV_ERROR(dev, "%s: failed to map phy base\n", __func__);
 159                return -ENOMEM;
 160        }
 161
 162        phy->pdev = pdev;
 163
 164        ret = msm_hdmi_phy_resource_init(phy);
 165        if (ret)
 166                return ret;
 167
 168        pm_runtime_enable(&pdev->dev);
 169
 170        ret = msm_hdmi_phy_resource_enable(phy);
 171        if (ret)
 172                return ret;
 173
 174        ret = msm_hdmi_phy_pll_init(pdev, phy->cfg->type);
 175        if (ret) {
 176                DRM_DEV_ERROR(dev, "couldn't init PLL\n");
 177                msm_hdmi_phy_resource_disable(phy);
 178                return ret;
 179        }
 180
 181        msm_hdmi_phy_resource_disable(phy);
 182
 183        platform_set_drvdata(pdev, phy);
 184
 185        return 0;
 186}
 187
 188static int msm_hdmi_phy_remove(struct platform_device *pdev)
 189{
 190        pm_runtime_disable(&pdev->dev);
 191
 192        return 0;
 193}
 194
 195static const struct of_device_id msm_hdmi_phy_dt_match[] = {
 196        { .compatible = "qcom,hdmi-phy-8660",
 197          .data = &msm_hdmi_phy_8x60_cfg },
 198        { .compatible = "qcom,hdmi-phy-8960",
 199          .data = &msm_hdmi_phy_8960_cfg },
 200        { .compatible = "qcom,hdmi-phy-8974",
 201          .data = &msm_hdmi_phy_8x74_cfg },
 202        { .compatible = "qcom,hdmi-phy-8084",
 203          .data = &msm_hdmi_phy_8x74_cfg },
 204        { .compatible = "qcom,hdmi-phy-8996",
 205          .data = &msm_hdmi_phy_8996_cfg },
 206        {}
 207};
 208
 209static struct platform_driver msm_hdmi_phy_platform_driver = {
 210        .probe      = msm_hdmi_phy_probe,
 211        .remove     = msm_hdmi_phy_remove,
 212        .driver     = {
 213                .name   = "msm_hdmi_phy",
 214                .of_match_table = msm_hdmi_phy_dt_match,
 215        },
 216};
 217
 218void __init msm_hdmi_phy_driver_register(void)
 219{
 220        platform_driver_register(&msm_hdmi_phy_platform_driver);
 221}
 222
 223void __exit msm_hdmi_phy_driver_unregister(void)
 224{
 225        platform_driver_unregister(&msm_hdmi_phy_platform_driver);
 226}
 227