xref: /openbmc/linux/drivers/gpu/drm/msm/hdmi/hdmi_phy.c (revision 722d4f06)
197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
215b4a452SArchit Taneja /*
315b4a452SArchit Taneja  * Copyright (c) 2016, The Linux Foundation. All rights reserved.
415b4a452SArchit Taneja  */
515b4a452SArchit Taneja 
6*722d4f06SRob Herring #include <linux/of.h>
7*722d4f06SRob Herring #include <linux/platform_device.h>
815b4a452SArchit Taneja 
915b4a452SArchit Taneja #include "hdmi.h"
1015b4a452SArchit Taneja 
msm_hdmi_phy_resource_init(struct hdmi_phy * phy)11fcda50c8SArnd Bergmann static int msm_hdmi_phy_resource_init(struct hdmi_phy *phy)
1215b4a452SArchit Taneja {
1315b4a452SArchit Taneja 	struct hdmi_phy_cfg *cfg = phy->cfg;
1415b4a452SArchit Taneja 	struct device *dev = &phy->pdev->dev;
1515b4a452SArchit Taneja 	int i, ret;
1615b4a452SArchit Taneja 
17a86854d0SKees Cook 	phy->regs = devm_kcalloc(dev, cfg->num_regs, sizeof(phy->regs[0]),
1815b4a452SArchit Taneja 				 GFP_KERNEL);
1915b4a452SArchit Taneja 	if (!phy->regs)
2015b4a452SArchit Taneja 		return -ENOMEM;
2115b4a452SArchit Taneja 
22a86854d0SKees Cook 	phy->clks = devm_kcalloc(dev, cfg->num_clks, sizeof(phy->clks[0]),
2315b4a452SArchit Taneja 				 GFP_KERNEL);
2415b4a452SArchit Taneja 	if (!phy->clks)
2515b4a452SArchit Taneja 		return -ENOMEM;
2615b4a452SArchit Taneja 
2731b3b1f5SDmitry Baryshkov 	for (i = 0; i < cfg->num_regs; i++)
2831b3b1f5SDmitry Baryshkov 		phy->regs[i].supply = cfg->reg_names[i];
2915b4a452SArchit Taneja 
3031b3b1f5SDmitry Baryshkov 	ret = devm_regulator_bulk_get(dev, cfg->num_regs, phy->regs);
3131b3b1f5SDmitry Baryshkov 	if (ret) {
3231b3b1f5SDmitry Baryshkov 		if (ret != -EPROBE_DEFER)
3331b3b1f5SDmitry Baryshkov 			DRM_DEV_ERROR(dev, "failed to get phy regulators: %d\n", ret);
34fd6c798bSBrian Masney 
3515b4a452SArchit Taneja 		return ret;
3615b4a452SArchit Taneja 	}
3715b4a452SArchit Taneja 
3815b4a452SArchit Taneja 	for (i = 0; i < cfg->num_clks; i++) {
3915b4a452SArchit Taneja 		struct clk *clk;
4015b4a452SArchit Taneja 
41aede1e9eSRob Clark 		clk = msm_clk_get(phy->pdev, cfg->clk_names[i]);
4215b4a452SArchit Taneja 		if (IS_ERR(clk)) {
4315b4a452SArchit Taneja 			ret = PTR_ERR(clk);
446a41da17SMamta Shukla 			DRM_DEV_ERROR(dev, "failed to get phy clock: %s (%d)\n",
4515b4a452SArchit Taneja 				cfg->clk_names[i], ret);
4615b4a452SArchit Taneja 			return ret;
4715b4a452SArchit Taneja 		}
4815b4a452SArchit Taneja 
4915b4a452SArchit Taneja 		phy->clks[i] = clk;
5015b4a452SArchit Taneja 	}
5115b4a452SArchit Taneja 
5215b4a452SArchit Taneja 	return 0;
5315b4a452SArchit Taneja }
5415b4a452SArchit Taneja 
msm_hdmi_phy_resource_enable(struct hdmi_phy * phy)55fcda50c8SArnd Bergmann int msm_hdmi_phy_resource_enable(struct hdmi_phy *phy)
5615b4a452SArchit Taneja {
5715b4a452SArchit Taneja 	struct hdmi_phy_cfg *cfg = phy->cfg;
5815b4a452SArchit Taneja 	struct device *dev = &phy->pdev->dev;
5915b4a452SArchit Taneja 	int i, ret = 0;
6015b4a452SArchit Taneja 
6115b4a452SArchit Taneja 	pm_runtime_get_sync(dev);
6215b4a452SArchit Taneja 
6331b3b1f5SDmitry Baryshkov 	ret = regulator_bulk_enable(cfg->num_regs, phy->regs);
6431b3b1f5SDmitry Baryshkov 	if (ret) {
6531b3b1f5SDmitry Baryshkov 		DRM_DEV_ERROR(dev, "failed to enable regulators: (%d)\n", ret);
6631b3b1f5SDmitry Baryshkov 		return ret;
6715b4a452SArchit Taneja 	}
6815b4a452SArchit Taneja 
6915b4a452SArchit Taneja 	for (i = 0; i < cfg->num_clks; i++) {
7015b4a452SArchit Taneja 		ret = clk_prepare_enable(phy->clks[i]);
7115b4a452SArchit Taneja 		if (ret)
726a41da17SMamta Shukla 			DRM_DEV_ERROR(dev, "failed to enable clock: %s (%d)\n",
7315b4a452SArchit Taneja 				cfg->clk_names[i], ret);
7415b4a452SArchit Taneja 	}
7515b4a452SArchit Taneja 
7615b4a452SArchit Taneja 	return ret;
7715b4a452SArchit Taneja }
7815b4a452SArchit Taneja 
msm_hdmi_phy_resource_disable(struct hdmi_phy * phy)79fcda50c8SArnd Bergmann void msm_hdmi_phy_resource_disable(struct hdmi_phy *phy)
8015b4a452SArchit Taneja {
8115b4a452SArchit Taneja 	struct hdmi_phy_cfg *cfg = phy->cfg;
8215b4a452SArchit Taneja 	struct device *dev = &phy->pdev->dev;
8315b4a452SArchit Taneja 	int i;
8415b4a452SArchit Taneja 
8515b4a452SArchit Taneja 	for (i = cfg->num_clks - 1; i >= 0; i--)
8615b4a452SArchit Taneja 		clk_disable_unprepare(phy->clks[i]);
8715b4a452SArchit Taneja 
8831b3b1f5SDmitry Baryshkov 	regulator_bulk_disable(cfg->num_regs, phy->regs);
8915b4a452SArchit Taneja 
9015b4a452SArchit Taneja 	pm_runtime_put_sync(dev);
9115b4a452SArchit Taneja }
9215b4a452SArchit Taneja 
msm_hdmi_phy_powerup(struct hdmi_phy * phy,unsigned long int pixclock)93fcda50c8SArnd Bergmann void msm_hdmi_phy_powerup(struct hdmi_phy *phy, unsigned long int pixclock)
9415b4a452SArchit Taneja {
9515b4a452SArchit Taneja 	if (!phy || !phy->cfg->powerup)
9615b4a452SArchit Taneja 		return;
9715b4a452SArchit Taneja 
9815b4a452SArchit Taneja 	phy->cfg->powerup(phy, pixclock);
9915b4a452SArchit Taneja }
10015b4a452SArchit Taneja 
msm_hdmi_phy_powerdown(struct hdmi_phy * phy)101fcda50c8SArnd Bergmann void msm_hdmi_phy_powerdown(struct hdmi_phy *phy)
10215b4a452SArchit Taneja {
10315b4a452SArchit Taneja 	if (!phy || !phy->cfg->powerdown)
10415b4a452SArchit Taneja 		return;
10515b4a452SArchit Taneja 
10615b4a452SArchit Taneja 	phy->cfg->powerdown(phy);
10715b4a452SArchit Taneja }
10815b4a452SArchit Taneja 
msm_hdmi_phy_pll_init(struct platform_device * pdev,enum hdmi_phy_type type)109fcda50c8SArnd Bergmann static int msm_hdmi_phy_pll_init(struct platform_device *pdev,
110ea184891SArchit Taneja 			     enum hdmi_phy_type type)
111ea184891SArchit Taneja {
112ea184891SArchit Taneja 	int ret;
113ea184891SArchit Taneja 
114ea184891SArchit Taneja 	switch (type) {
115ea184891SArchit Taneja 	case MSM_HDMI_PHY_8960:
116fcda50c8SArnd Bergmann 		ret = msm_hdmi_pll_8960_init(pdev);
117ea184891SArchit Taneja 		break;
118e17afdceSArchit Taneja 	case MSM_HDMI_PHY_8996:
119fcda50c8SArnd Bergmann 		ret = msm_hdmi_pll_8996_init(pdev);
120e17afdceSArchit Taneja 		break;
121ea184891SArchit Taneja 	/*
122ea184891SArchit Taneja 	 * we don't have PLL support for these, don't report an error for now
123ea184891SArchit Taneja 	 */
124ea184891SArchit Taneja 	case MSM_HDMI_PHY_8x60:
125ea184891SArchit Taneja 	case MSM_HDMI_PHY_8x74:
126ea184891SArchit Taneja 	default:
127ea184891SArchit Taneja 		ret = 0;
128ea184891SArchit Taneja 		break;
129ea184891SArchit Taneja 	}
130ea184891SArchit Taneja 
131ea184891SArchit Taneja 	return ret;
132ea184891SArchit Taneja }
133ea184891SArchit Taneja 
msm_hdmi_phy_probe(struct platform_device * pdev)134fcda50c8SArnd Bergmann static int msm_hdmi_phy_probe(struct platform_device *pdev)
13515b4a452SArchit Taneja {
13615b4a452SArchit Taneja 	struct device *dev = &pdev->dev;
13715b4a452SArchit Taneja 	struct hdmi_phy *phy;
13815b4a452SArchit Taneja 	int ret;
13915b4a452SArchit Taneja 
14015b4a452SArchit Taneja 	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
14115b4a452SArchit Taneja 	if (!phy)
14215b4a452SArchit Taneja 		return -ENODEV;
14315b4a452SArchit Taneja 
14415b4a452SArchit Taneja 	phy->cfg = (struct hdmi_phy_cfg *)of_device_get_match_data(dev);
14515b4a452SArchit Taneja 	if (!phy->cfg)
14615b4a452SArchit Taneja 		return -ENODEV;
14715b4a452SArchit Taneja 
148c0e745d7SDmitry Baryshkov 	phy->mmio = msm_ioremap(pdev, "hdmi_phy");
14915b4a452SArchit Taneja 	if (IS_ERR(phy->mmio)) {
1506a41da17SMamta Shukla 		DRM_DEV_ERROR(dev, "%s: failed to map phy base\n", __func__);
15115b4a452SArchit Taneja 		return -ENOMEM;
15215b4a452SArchit Taneja 	}
15315b4a452SArchit Taneja 
15415b4a452SArchit Taneja 	phy->pdev = pdev;
15515b4a452SArchit Taneja 
156fcda50c8SArnd Bergmann 	ret = msm_hdmi_phy_resource_init(phy);
15715b4a452SArchit Taneja 	if (ret)
15815b4a452SArchit Taneja 		return ret;
15915b4a452SArchit Taneja 
16015b4a452SArchit Taneja 	pm_runtime_enable(&pdev->dev);
16115b4a452SArchit Taneja 
162fcda50c8SArnd Bergmann 	ret = msm_hdmi_phy_resource_enable(phy);
163ea184891SArchit Taneja 	if (ret)
164ea184891SArchit Taneja 		return ret;
165ea184891SArchit Taneja 
166fcda50c8SArnd Bergmann 	ret = msm_hdmi_phy_pll_init(pdev, phy->cfg->type);
167ea184891SArchit Taneja 	if (ret) {
1686a41da17SMamta Shukla 		DRM_DEV_ERROR(dev, "couldn't init PLL\n");
169fcda50c8SArnd Bergmann 		msm_hdmi_phy_resource_disable(phy);
170ea184891SArchit Taneja 		return ret;
171ea184891SArchit Taneja 	}
172ea184891SArchit Taneja 
173fcda50c8SArnd Bergmann 	msm_hdmi_phy_resource_disable(phy);
174ea184891SArchit Taneja 
17515b4a452SArchit Taneja 	platform_set_drvdata(pdev, phy);
17615b4a452SArchit Taneja 
17715b4a452SArchit Taneja 	return 0;
17815b4a452SArchit Taneja }
17915b4a452SArchit Taneja 
msm_hdmi_phy_remove(struct platform_device * pdev)180fcda50c8SArnd Bergmann static int msm_hdmi_phy_remove(struct platform_device *pdev)
18115b4a452SArchit Taneja {
18215b4a452SArchit Taneja 	pm_runtime_disable(&pdev->dev);
18315b4a452SArchit Taneja 
18415b4a452SArchit Taneja 	return 0;
18515b4a452SArchit Taneja }
18615b4a452SArchit Taneja 
187fcda50c8SArnd Bergmann static const struct of_device_id msm_hdmi_phy_dt_match[] = {
18815b4a452SArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8660",
189fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8x60_cfg },
19015b4a452SArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8960",
191fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8960_cfg },
19215b4a452SArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8974",
193fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8x74_cfg },
19415b4a452SArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8084",
195fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8x74_cfg },
196e17afdceSArchit Taneja 	{ .compatible = "qcom,hdmi-phy-8996",
197fcda50c8SArnd Bergmann 	  .data = &msm_hdmi_phy_8996_cfg },
19815b4a452SArchit Taneja 	{}
19915b4a452SArchit Taneja };
20015b4a452SArchit Taneja 
201fcda50c8SArnd Bergmann static struct platform_driver msm_hdmi_phy_platform_driver = {
202fcda50c8SArnd Bergmann 	.probe      = msm_hdmi_phy_probe,
203fcda50c8SArnd Bergmann 	.remove     = msm_hdmi_phy_remove,
20415b4a452SArchit Taneja 	.driver     = {
20515b4a452SArchit Taneja 		.name   = "msm_hdmi_phy",
206fcda50c8SArnd Bergmann 		.of_match_table = msm_hdmi_phy_dt_match,
20715b4a452SArchit Taneja 	},
20815b4a452SArchit Taneja };
20915b4a452SArchit Taneja 
msm_hdmi_phy_driver_register(void)210fcda50c8SArnd Bergmann void __init msm_hdmi_phy_driver_register(void)
21115b4a452SArchit Taneja {
212fcda50c8SArnd Bergmann 	platform_driver_register(&msm_hdmi_phy_platform_driver);
21315b4a452SArchit Taneja }
21415b4a452SArchit Taneja 
msm_hdmi_phy_driver_unregister(void)215fcda50c8SArnd Bergmann void __exit msm_hdmi_phy_driver_unregister(void)
21615b4a452SArchit Taneja {
217fcda50c8SArnd Bergmann 	platform_driver_unregister(&msm_hdmi_phy_platform_driver);
21815b4a452SArchit Taneja }
219