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 | |
9 | static int mtk_hdmi_phy_power_on(struct phy *phy); |
10 | static int mtk_hdmi_phy_power_off(struct phy *phy); |
11 | static int mtk_hdmi_phy_configure(struct phy *phy, union phy_configure_opts *opts); |
12 | |
13 | static const struct phy_ops mtk_hdmi_phy_dev_ops = { |
14 | .power_on = mtk_hdmi_phy_power_on, |
15 | .power_off = mtk_hdmi_phy_power_off, |
16 | .configure = mtk_hdmi_phy_configure, |
17 | .owner = THIS_MODULE, |
18 | }; |
19 | |
20 | inline struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw) |
21 | { |
22 | return container_of(hw, struct mtk_hdmi_phy, pll_hw); |
23 | } |
24 | |
25 | static int mtk_hdmi_phy_power_on(struct phy *phy) |
26 | { |
27 | struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); |
28 | int ret; |
29 | |
30 | ret = clk_prepare_enable(clk: hdmi_phy->pll); |
31 | if (ret < 0) |
32 | return ret; |
33 | |
34 | hdmi_phy->conf->hdmi_phy_enable_tmds(hdmi_phy); |
35 | return 0; |
36 | } |
37 | |
38 | static int mtk_hdmi_phy_power_off(struct phy *phy) |
39 | { |
40 | struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); |
41 | |
42 | hdmi_phy->conf->hdmi_phy_disable_tmds(hdmi_phy); |
43 | clk_disable_unprepare(clk: hdmi_phy->pll); |
44 | |
45 | return 0; |
46 | } |
47 | |
48 | static int mtk_hdmi_phy_configure(struct phy *phy, union phy_configure_opts *opts) |
49 | { |
50 | struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); |
51 | |
52 | if (hdmi_phy->conf->hdmi_phy_configure) |
53 | return hdmi_phy->conf->hdmi_phy_configure(phy, opts); |
54 | |
55 | return 0; |
56 | } |
57 | |
58 | static const struct phy_ops * |
59 | mtk_hdmi_phy_dev_get_ops(const struct mtk_hdmi_phy *hdmi_phy) |
60 | { |
61 | if (hdmi_phy && hdmi_phy->conf && |
62 | hdmi_phy->conf->hdmi_phy_enable_tmds && |
63 | hdmi_phy->conf->hdmi_phy_disable_tmds) |
64 | return &mtk_hdmi_phy_dev_ops; |
65 | |
66 | if (hdmi_phy) |
67 | dev_err(hdmi_phy->dev, "Failed to get dev ops of phy\n" ); |
68 | return NULL; |
69 | } |
70 | |
71 | static void mtk_hdmi_phy_clk_get_data(struct mtk_hdmi_phy *hdmi_phy, |
72 | struct clk_init_data *clk_init) |
73 | { |
74 | clk_init->flags = hdmi_phy->conf->flags; |
75 | clk_init->ops = hdmi_phy->conf->hdmi_phy_clk_ops; |
76 | } |
77 | |
78 | static int mtk_hdmi_phy_probe(struct platform_device *pdev) |
79 | { |
80 | struct device *dev = &pdev->dev; |
81 | struct mtk_hdmi_phy *hdmi_phy; |
82 | struct clk *ref_clk; |
83 | const char *ref_clk_name; |
84 | struct clk_init_data clk_init = { |
85 | .num_parents = 1, |
86 | .parent_names = (const char * const *)&ref_clk_name, |
87 | }; |
88 | |
89 | struct phy *phy; |
90 | struct phy_provider *phy_provider; |
91 | int ret; |
92 | |
93 | hdmi_phy = devm_kzalloc(dev, size: sizeof(*hdmi_phy), GFP_KERNEL); |
94 | if (!hdmi_phy) |
95 | return -ENOMEM; |
96 | |
97 | hdmi_phy->regs = devm_platform_ioremap_resource(pdev, index: 0); |
98 | if (IS_ERR(ptr: hdmi_phy->regs)) |
99 | return PTR_ERR(ptr: hdmi_phy->regs); |
100 | |
101 | ref_clk = devm_clk_get(dev, id: "pll_ref" ); |
102 | if (IS_ERR(ptr: ref_clk)) |
103 | return dev_err_probe(dev, err: PTR_ERR(ptr: ref_clk), |
104 | fmt: "Failed to get PLL reference clock\n" ); |
105 | |
106 | ref_clk_name = __clk_get_name(clk: ref_clk); |
107 | |
108 | ret = of_property_read_string(np: dev->of_node, propname: "clock-output-names" , |
109 | out_string: &clk_init.name); |
110 | if (ret < 0) |
111 | return dev_err_probe(dev, err: ret, fmt: "Failed to read clock-output-names\n" ); |
112 | |
113 | hdmi_phy->dev = dev; |
114 | hdmi_phy->conf = |
115 | (struct mtk_hdmi_phy_conf *)of_device_get_match_data(dev); |
116 | mtk_hdmi_phy_clk_get_data(hdmi_phy, clk_init: &clk_init); |
117 | hdmi_phy->pll_hw.init = &clk_init; |
118 | hdmi_phy->pll = devm_clk_register(dev, hw: &hdmi_phy->pll_hw); |
119 | if (IS_ERR(ptr: hdmi_phy->pll)) |
120 | return dev_err_probe(dev, err: PTR_ERR(ptr: hdmi_phy->pll), |
121 | fmt: "Failed to register PLL\n" ); |
122 | |
123 | ret = of_property_read_u32(np: dev->of_node, propname: "mediatek,ibias" , |
124 | out_value: &hdmi_phy->ibias); |
125 | if (ret < 0) |
126 | return dev_err_probe(dev, err: ret, fmt: "Failed to get ibias\n" ); |
127 | |
128 | ret = of_property_read_u32(np: dev->of_node, propname: "mediatek,ibias_up" , |
129 | out_value: &hdmi_phy->ibias_up); |
130 | if (ret < 0) |
131 | return dev_err_probe(dev, err: ret, fmt: "Failed to get ibias_up\n" ); |
132 | |
133 | dev_info(dev, "Using default TX DRV impedance: 4.2k/36\n" ); |
134 | hdmi_phy->drv_imp_clk = 0x30; |
135 | hdmi_phy->drv_imp_d2 = 0x30; |
136 | hdmi_phy->drv_imp_d1 = 0x30; |
137 | hdmi_phy->drv_imp_d0 = 0x30; |
138 | |
139 | phy = devm_phy_create(dev, NULL, ops: mtk_hdmi_phy_dev_get_ops(hdmi_phy)); |
140 | if (IS_ERR(ptr: phy)) |
141 | return dev_err_probe(dev, err: PTR_ERR(ptr: phy), fmt: "Cannot create HDMI PHY\n" ); |
142 | |
143 | phy_set_drvdata(phy, data: hdmi_phy); |
144 | |
145 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
146 | if (IS_ERR(ptr: phy_provider)) |
147 | return dev_err_probe(dev, err: PTR_ERR(ptr: phy_provider), |
148 | fmt: "Failed to register HDMI PHY\n" ); |
149 | |
150 | if (hdmi_phy->conf->pll_default_off) |
151 | hdmi_phy->conf->hdmi_phy_disable_tmds(hdmi_phy); |
152 | |
153 | return of_clk_add_provider(np: dev->of_node, clk_src_get: of_clk_src_simple_get, |
154 | data: hdmi_phy->pll); |
155 | } |
156 | |
157 | static const struct of_device_id mtk_hdmi_phy_match[] = { |
158 | { .compatible = "mediatek,mt2701-hdmi-phy" , |
159 | .data = &mtk_hdmi_phy_2701_conf, |
160 | }, |
161 | { .compatible = "mediatek,mt8173-hdmi-phy" , |
162 | .data = &mtk_hdmi_phy_8173_conf, |
163 | }, |
164 | { .compatible = "mediatek,mt8195-hdmi-phy" , |
165 | .data = &mtk_hdmi_phy_8195_conf, |
166 | }, |
167 | {}, |
168 | }; |
169 | MODULE_DEVICE_TABLE(of, mtk_hdmi_phy_match); |
170 | |
171 | static struct platform_driver mtk_hdmi_phy_driver = { |
172 | .probe = mtk_hdmi_phy_probe, |
173 | .driver = { |
174 | .name = "mediatek-hdmi-phy" , |
175 | .of_match_table = mtk_hdmi_phy_match, |
176 | }, |
177 | }; |
178 | module_platform_driver(mtk_hdmi_phy_driver); |
179 | |
180 | MODULE_DESCRIPTION("MediaTek HDMI PHY Driver" ); |
181 | MODULE_LICENSE("GPL v2" ); |
182 | |