xref: /OK3568_Linux_fs/kernel/drivers/phy/rockchip/phy-rockchip-inno-video-phy.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2018 Rockchip Electronics Co. Ltd.
4  *
5  * Author: Wyon Bi <bivvy.bi@rock-chips.com>
6  */
7 
8 #include <linux/kernel.h>
9 #include <linux/clk.h>
10 #include <linux/delay.h>
11 #include <linux/init.h>
12 #include <linux/module.h>
13 #include <linux/of_device.h>
14 #include <linux/platform_device.h>
15 #include <linux/regmap.h>
16 #include <linux/reset.h>
17 #include <linux/phy/phy.h>
18 #include <linux/pm_runtime.h>
19 
20 /* 0x0030 */
21 #define DISABLE_PLL	BIT(3)
22 /* 0x003c */
23 #define PLL_LOCK	BIT(1)
24 /* 0x0084 */
25 #define ENABLE_TX	BIT(7)
26 
27 struct inno_video_phy {
28 	struct device *dev;
29 	struct clk *pclk;
30 	struct regmap *regmap;
31 	struct reset_control *rst;
32 };
33 
34 static const struct reg_sequence ttl_mode[] = {
35 	{ 0x0000, 0x7f },
36 	{ 0x0004, 0x3f },
37 	{ 0x0008, 0x80 },
38 	{ 0x0010, 0x3f },
39 	{ 0x0014, 0x3f },
40 	{ 0x0080, 0x44 },
41 
42 	{ 0x0100, 0x7f },
43 	{ 0x0104, 0x3f },
44 	{ 0x0108, 0x80 },
45 	{ 0x0110, 0x3f },
46 	{ 0x0114, 0x3f },
47 	{ 0x0180, 0x44 },
48 };
49 
50 static const struct reg_sequence lvds_mode_single_channel[] = {
51 	{ 0x0000, 0xbf },
52 	{ 0x0004, 0x3f },
53 	{ 0x0008, 0xfe },
54 	{ 0x0010, 0x00 },
55 	{ 0x0014, 0x00 },
56 	{ 0x0080, 0x44 },
57 
58 	{ 0x0100, 0x00 },
59 	{ 0x0104, 0x00 },
60 	{ 0x0108, 0x00 },
61 	{ 0x0110, 0x00 },
62 	{ 0x0114, 0x00 },
63 	{ 0x0180, 0x44 },
64 };
65 
66 static const struct reg_sequence lvds_mode_dual_channel[] = {
67 	{ 0x0000, 0xbf },
68 	{ 0x0004, 0x3f },
69 	{ 0x0008, 0xfe },
70 	{ 0x0010, 0x00 },
71 	{ 0x0014, 0x00 },
72 	{ 0x0080, 0x44 },
73 
74 	{ 0x0100, 0xbf },
75 	{ 0x0104, 0x3f },
76 	{ 0x0108, 0xfe },
77 	{ 0x0110, 0x00 },
78 	{ 0x0114, 0x00 },
79 	{ 0x0180, 0x44 },
80 };
81 
inno_video_phy_power_on(struct phy * phy)82 static int inno_video_phy_power_on(struct phy *phy)
83 {
84 	struct inno_video_phy *inno = phy_get_drvdata(phy);
85 	enum phy_mode mode = phy_get_mode(phy);
86 	const struct reg_sequence *wseq;
87 	bool dual_channel = phy_get_bus_width(phy) == 2 ? true : false;
88 	int nregs;
89 	u32 status;
90 	int ret;
91 
92 	clk_prepare_enable(inno->pclk);
93 	pm_runtime_get_sync(inno->dev);
94 
95 	switch (mode) {
96 	case PHY_MODE_LVDS:
97 		if (dual_channel) {
98 			wseq = lvds_mode_dual_channel;
99 			nregs = ARRAY_SIZE(lvds_mode_dual_channel);
100 		} else {
101 			wseq = lvds_mode_single_channel;
102 			nregs = ARRAY_SIZE(lvds_mode_single_channel);
103 		}
104 		break;
105 	default:
106 		wseq = ttl_mode;
107 		nregs = ARRAY_SIZE(ttl_mode);
108 		break;
109 	}
110 
111 	regmap_multi_reg_write(inno->regmap, wseq, nregs);
112 
113 	regmap_update_bits(inno->regmap, 0x0030, DISABLE_PLL, 0);
114 	ret = regmap_read_poll_timeout(inno->regmap, 0x003c, status,
115 				       status & PLL_LOCK, 50, 5000);
116 	if (ret) {
117 		dev_err(inno->dev, "PLL is not lock\n");
118 		return ret;
119 	}
120 
121 	regmap_update_bits(inno->regmap, 0x0084, ENABLE_TX, ENABLE_TX);
122 
123 	return 0;
124 }
125 
inno_video_phy_power_off(struct phy * phy)126 static int inno_video_phy_power_off(struct phy *phy)
127 {
128 	struct inno_video_phy *inno = phy_get_drvdata(phy);
129 
130 	regmap_update_bits(inno->regmap, 0x0084, ENABLE_TX, 0);
131 	regmap_update_bits(inno->regmap, 0x0030, DISABLE_PLL, DISABLE_PLL);
132 
133 	pm_runtime_put(inno->dev);
134 	clk_disable_unprepare(inno->pclk);
135 
136 	return 0;
137 }
138 
inno_video_phy_set_mode(struct phy * phy,enum phy_mode mode)139 static int inno_video_phy_set_mode(struct phy *phy, enum phy_mode mode)
140 {
141 	return 0;
142 }
143 
144 static const struct phy_ops inno_video_phy_ops = {
145 	.set_mode = inno_video_phy_set_mode,
146 	.power_on = inno_video_phy_power_on,
147 	.power_off = inno_video_phy_power_off,
148 	.owner = THIS_MODULE,
149 };
150 
151 static const struct regmap_config inno_video_phy_regmap_config = {
152 	.reg_bits = 32,
153 	.val_bits = 32,
154 	.reg_stride = 4,
155 	.max_register = 0x0180,
156 };
157 
inno_video_phy_probe(struct platform_device * pdev)158 static int inno_video_phy_probe(struct platform_device *pdev)
159 {
160 	struct device *dev = &pdev->dev;
161 	struct inno_video_phy *inno;
162 	struct phy *phy;
163 	struct phy_provider *phy_provider;
164 	struct resource *res;
165 	void __iomem *regs;
166 	int ret;
167 
168 	inno = devm_kzalloc(dev, sizeof(*inno), GFP_KERNEL);
169 	if (!inno)
170 		return -ENOMEM;
171 
172 	inno->dev = dev;
173 	platform_set_drvdata(pdev, inno);
174 
175 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
176 	regs = devm_ioremap_resource(dev, res);
177 	if (IS_ERR(regs))
178 		return PTR_ERR(regs);
179 
180 	inno->regmap = devm_regmap_init_mmio(dev, regs,
181 					     &inno_video_phy_regmap_config);
182 	if (IS_ERR(inno->regmap)) {
183 		ret = PTR_ERR(inno->regmap);
184 		dev_err(dev, "failed to init regmap: %d\n", ret);
185 		return ret;
186 	}
187 
188 	inno->pclk = devm_clk_get(dev, "pclk");
189 	if (IS_ERR(inno->pclk)) {
190 		dev_err(dev, "failed to get pclk\n");
191 		return PTR_ERR(inno->pclk);
192 	}
193 
194 	inno->rst = devm_reset_control_get(dev, "rst");
195 	if (IS_ERR(inno->rst)) {
196 		dev_err(dev, "failed to get reset control\n");
197 		return PTR_ERR(inno->rst);
198 	}
199 
200 	phy = devm_phy_create(dev, NULL, &inno_video_phy_ops);
201 	if (IS_ERR(phy)) {
202 		ret = PTR_ERR(phy);
203 		dev_err(dev, "failed to create PHY: %d\n", ret);
204 		return ret;
205 	}
206 
207 	phy_set_drvdata(phy, inno);
208 
209 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
210 	if (IS_ERR(phy_provider)) {
211 		dev_err(dev, "failed to register phy provider\n");
212 		return PTR_ERR(phy_provider);
213 	}
214 
215 	pm_runtime_enable(dev);
216 
217 	return 0;
218 }
219 
inno_video_phy_remove(struct platform_device * pdev)220 static int inno_video_phy_remove(struct platform_device *pdev)
221 {
222 	pm_runtime_disable(&pdev->dev);
223 
224 	return 0;
225 }
226 
227 static const struct of_device_id inno_video_phy_of_match[] = {
228 	{ .compatible = "rockchip,rk3288-video-phy", },
229 	{}
230 };
231 MODULE_DEVICE_TABLE(of, inno_video_phy_of_match);
232 
233 static struct platform_driver inno_video_phy_driver = {
234 	.driver = {
235 		.name = "inno-video-phy",
236 		.of_match_table	= of_match_ptr(inno_video_phy_of_match),
237 	},
238 	.probe = inno_video_phy_probe,
239 	.remove = inno_video_phy_remove,
240 };
241 module_platform_driver(inno_video_phy_driver);
242 
243 MODULE_AUTHOR("Wyon Bi <bivvy.bi@rock-chips.com>");
244 MODULE_DESCRIPTION("Innosilicon LVDS/TTL PHY driver");
245 MODULE_LICENSE("GPL v2");
246