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