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