Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (c) 2018 MediaTek Inc.
  4 * Author: Jie Qiu <jie.qiu@mediatek.com>
  5 */
  6
  7#include "phy-mtk-hdmi.h"
  8
  9static int mtk_hdmi_phy_power_on(struct phy *phy);
 10static int mtk_hdmi_phy_power_off(struct phy *phy);
 11
 12static const struct phy_ops mtk_hdmi_phy_dev_ops = {
 13	.power_on = mtk_hdmi_phy_power_on,
 14	.power_off = mtk_hdmi_phy_power_off,
 15	.owner = THIS_MODULE,
 16};
 17
 18void mtk_hdmi_phy_clear_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset,
 19			     u32 bits)
 20{
 21	void __iomem *reg = hdmi_phy->regs + offset;
 22	u32 tmp;
 23
 24	tmp = readl(reg);
 25	tmp &= ~bits;
 26	writel(tmp, reg);
 27}
 28
 29void mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset,
 30			   u32 bits)
 31{
 32	void __iomem *reg = hdmi_phy->regs + offset;
 33	u32 tmp;
 34
 35	tmp = readl(reg);
 36	tmp |= bits;
 37	writel(tmp, reg);
 38}
 39
 40void mtk_hdmi_phy_mask(struct mtk_hdmi_phy *hdmi_phy, u32 offset,
 41		       u32 val, u32 mask)
 42{
 43	void __iomem *reg = hdmi_phy->regs + offset;
 44	u32 tmp;
 45
 46	tmp = readl(reg);
 47	tmp = (tmp & ~mask) | (val & mask);
 48	writel(tmp, reg);
 49}
 50
 51inline struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw)
 52{
 53	return container_of(hw, struct mtk_hdmi_phy, pll_hw);
 54}
 55
 56static int mtk_hdmi_phy_power_on(struct phy *phy)
 57{
 58	struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy);
 59	int ret;
 60
 61	ret = clk_prepare_enable(hdmi_phy->pll);
 62	if (ret < 0)
 63		return ret;
 64
 65	hdmi_phy->conf->hdmi_phy_enable_tmds(hdmi_phy);
 66	return 0;
 67}
 68
 69static int mtk_hdmi_phy_power_off(struct phy *phy)
 70{
 71	struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy);
 72
 73	hdmi_phy->conf->hdmi_phy_disable_tmds(hdmi_phy);
 74	clk_disable_unprepare(hdmi_phy->pll);
 75
 76	return 0;
 77}
 78
 79static const struct phy_ops *
 80mtk_hdmi_phy_dev_get_ops(const struct mtk_hdmi_phy *hdmi_phy)
 81{
 82	if (hdmi_phy && hdmi_phy->conf &&
 83	    hdmi_phy->conf->hdmi_phy_enable_tmds &&
 84	    hdmi_phy->conf->hdmi_phy_disable_tmds)
 85		return &mtk_hdmi_phy_dev_ops;
 86
 87	if (hdmi_phy)
 88		dev_err(hdmi_phy->dev, "Failed to get dev ops of phy\n");
 89	return NULL;
 90}
 91
 92static void mtk_hdmi_phy_clk_get_data(struct mtk_hdmi_phy *hdmi_phy,
 93				      struct clk_init_data *clk_init)
 94{
 95	clk_init->flags = hdmi_phy->conf->flags;
 96	clk_init->ops = hdmi_phy->conf->hdmi_phy_clk_ops;
 97}
 98
 99static int mtk_hdmi_phy_probe(struct platform_device *pdev)
100{
101	struct device *dev = &pdev->dev;
102	struct mtk_hdmi_phy *hdmi_phy;
103	struct resource *mem;
104	struct clk *ref_clk;
105	const char *ref_clk_name;
106	struct clk_init_data clk_init = {
107		.num_parents = 1,
108		.parent_names = (const char * const *)&ref_clk_name,
109	};
110
111	struct phy *phy;
112	struct phy_provider *phy_provider;
113	int ret;
114
115	hdmi_phy = devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL);
116	if (!hdmi_phy)
117		return -ENOMEM;
118
119	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
120	hdmi_phy->regs = devm_ioremap_resource(dev, mem);
121	if (IS_ERR(hdmi_phy->regs)) {
122		return PTR_ERR(hdmi_phy->regs);
123	}
124
125	ref_clk = devm_clk_get(dev, "pll_ref");
126	if (IS_ERR(ref_clk)) {
127		ret = PTR_ERR(ref_clk);
128		dev_err(&pdev->dev, "Failed to get PLL reference clock: %d\n",
129			ret);
130		return ret;
131	}
132	ref_clk_name = __clk_get_name(ref_clk);
133
134	ret = of_property_read_string(dev->of_node, "clock-output-names",
135				      &clk_init.name);
136	if (ret < 0) {
137		dev_err(dev, "Failed to read clock-output-names: %d\n", ret);
138		return ret;
139	}
140
141	hdmi_phy->dev = dev;
142	hdmi_phy->conf =
143		(struct mtk_hdmi_phy_conf *)of_device_get_match_data(dev);
144	mtk_hdmi_phy_clk_get_data(hdmi_phy, &clk_init);
145	hdmi_phy->pll_hw.init = &clk_init;
146	hdmi_phy->pll = devm_clk_register(dev, &hdmi_phy->pll_hw);
147	if (IS_ERR(hdmi_phy->pll)) {
148		ret = PTR_ERR(hdmi_phy->pll);
149		dev_err(dev, "Failed to register PLL: %d\n", ret);
150		return ret;
151	}
152
153	ret = of_property_read_u32(dev->of_node, "mediatek,ibias",
154				   &hdmi_phy->ibias);
155	if (ret < 0) {
156		dev_err(&pdev->dev, "Failed to get ibias: %d\n", ret);
157		return ret;
158	}
159
160	ret = of_property_read_u32(dev->of_node, "mediatek,ibias_up",
161				   &hdmi_phy->ibias_up);
162	if (ret < 0) {
163		dev_err(&pdev->dev, "Failed to get ibias up: %d\n", ret);
164		return ret;
165	}
166
167	dev_info(dev, "Using default TX DRV impedance: 4.2k/36\n");
168	hdmi_phy->drv_imp_clk = 0x30;
169	hdmi_phy->drv_imp_d2 = 0x30;
170	hdmi_phy->drv_imp_d1 = 0x30;
171	hdmi_phy->drv_imp_d0 = 0x30;
172
173	phy = devm_phy_create(dev, NULL, mtk_hdmi_phy_dev_get_ops(hdmi_phy));
174	if (IS_ERR(phy)) {
175		dev_err(dev, "Failed to create HDMI PHY\n");
176		return PTR_ERR(phy);
177	}
178	phy_set_drvdata(phy, hdmi_phy);
179
180	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
181	if (IS_ERR(phy_provider)) {
182		dev_err(dev, "Failed to register HDMI PHY\n");
183		return PTR_ERR(phy_provider);
184	}
185
186	if (hdmi_phy->conf->pll_default_off)
187		hdmi_phy->conf->hdmi_phy_disable_tmds(hdmi_phy);
188
189	return of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
190				   hdmi_phy->pll);
191}
192
193static const struct of_device_id mtk_hdmi_phy_match[] = {
194	{ .compatible = "mediatek,mt2701-hdmi-phy",
195	  .data = &mtk_hdmi_phy_2701_conf,
196	},
197	{ .compatible = "mediatek,mt8173-hdmi-phy",
198	  .data = &mtk_hdmi_phy_8173_conf,
199	},
200	{},
201};
202MODULE_DEVICE_TABLE(of, mtk_hdmi_phy_match);
203
204static struct platform_driver mtk_hdmi_phy_driver = {
205	.probe = mtk_hdmi_phy_probe,
206	.driver = {
207		.name = "mediatek-hdmi-phy",
208		.of_match_table = mtk_hdmi_phy_match,
209	},
210};
211module_platform_driver(mtk_hdmi_phy_driver);
212
213MODULE_DESCRIPTION("MediaTek HDMI PHY Driver");
214MODULE_LICENSE("GPL v2");