Linux Audio

Check our new training course

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