1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2014 MediaTek Inc. |
4 | * Author: Jie Qiu <jie.qiu@mediatek.com> |
5 | */ |
6 | #include <linux/kernel.h> |
7 | #include <linux/module.h> |
8 | #include <linux/i2c.h> |
9 | #include <linux/time.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/errno.h> |
12 | #include <linux/err.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/clk.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/io.h> |
17 | #include <linux/iopoll.h> |
18 | #include <linux/of_address.h> |
19 | #include <linux/of_irq.h> |
20 | #include <linux/of_platform.h> |
21 | |
22 | #include "mtk_drm_drv.h" |
23 | #include "mtk_hdmi.h" |
24 | |
25 | #define SIF1_CLOK (288) |
26 | #define DDC_DDCMCTL0 (0x0) |
27 | #define DDCM_ODRAIN BIT(31) |
28 | #define DDCM_CLK_DIV_OFFSET (16) |
29 | #define DDCM_CLK_DIV_MASK (0xfff << 16) |
30 | #define DDCM_CS_STATUS BIT(4) |
31 | #define DDCM_SCL_STATE BIT(3) |
32 | #define DDCM_SDA_STATE BIT(2) |
33 | #define DDCM_SM0EN BIT(1) |
34 | #define DDCM_SCL_STRECH BIT(0) |
35 | #define DDC_DDCMCTL1 (0x4) |
36 | #define DDCM_ACK_OFFSET (16) |
37 | #define DDCM_ACK_MASK (0xff << 16) |
38 | #define DDCM_PGLEN_OFFSET (8) |
39 | #define DDCM_PGLEN_MASK (0x7 << 8) |
40 | #define DDCM_SIF_MODE_OFFSET (4) |
41 | #define DDCM_SIF_MODE_MASK (0x7 << 4) |
42 | #define DDCM_START (0x1) |
43 | #define DDCM_WRITE_DATA (0x2) |
44 | #define DDCM_STOP (0x3) |
45 | #define DDCM_READ_DATA_NO_ACK (0x4) |
46 | #define DDCM_READ_DATA_ACK (0x5) |
47 | #define DDCM_TRI BIT(0) |
48 | #define DDC_DDCMD0 (0x8) |
49 | #define DDCM_DATA3 (0xff << 24) |
50 | #define DDCM_DATA2 (0xff << 16) |
51 | #define DDCM_DATA1 (0xff << 8) |
52 | #define DDCM_DATA0 (0xff << 0) |
53 | #define DDC_DDCMD1 (0xc) |
54 | #define DDCM_DATA7 (0xff << 24) |
55 | #define DDCM_DATA6 (0xff << 16) |
56 | #define DDCM_DATA5 (0xff << 8) |
57 | #define DDCM_DATA4 (0xff << 0) |
58 | |
59 | struct mtk_hdmi_ddc { |
60 | struct i2c_adapter adap; |
61 | struct clk *clk; |
62 | void __iomem *regs; |
63 | }; |
64 | |
65 | static inline void sif_set_bit(struct mtk_hdmi_ddc *ddc, unsigned int offset, |
66 | unsigned int val) |
67 | { |
68 | writel(readl(addr: ddc->regs + offset) | val, addr: ddc->regs + offset); |
69 | } |
70 | |
71 | static inline void sif_clr_bit(struct mtk_hdmi_ddc *ddc, unsigned int offset, |
72 | unsigned int val) |
73 | { |
74 | writel(readl(addr: ddc->regs + offset) & ~val, addr: ddc->regs + offset); |
75 | } |
76 | |
77 | static inline bool sif_bit_is_set(struct mtk_hdmi_ddc *ddc, unsigned int offset, |
78 | unsigned int val) |
79 | { |
80 | return (readl(addr: ddc->regs + offset) & val) == val; |
81 | } |
82 | |
83 | static inline void sif_write_mask(struct mtk_hdmi_ddc *ddc, unsigned int offset, |
84 | unsigned int mask, unsigned int shift, |
85 | unsigned int val) |
86 | { |
87 | unsigned int tmp; |
88 | |
89 | tmp = readl(addr: ddc->regs + offset); |
90 | tmp &= ~mask; |
91 | tmp |= (val << shift) & mask; |
92 | writel(val: tmp, addr: ddc->regs + offset); |
93 | } |
94 | |
95 | static inline unsigned int sif_read_mask(struct mtk_hdmi_ddc *ddc, |
96 | unsigned int offset, unsigned int mask, |
97 | unsigned int shift) |
98 | { |
99 | return (readl(addr: ddc->regs + offset) & mask) >> shift; |
100 | } |
101 | |
102 | static void ddcm_trigger_mode(struct mtk_hdmi_ddc *ddc, int mode) |
103 | { |
104 | u32 val; |
105 | |
106 | sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_SIF_MODE_MASK, |
107 | DDCM_SIF_MODE_OFFSET, val: mode); |
108 | sif_set_bit(ddc, DDC_DDCMCTL1, DDCM_TRI); |
109 | readl_poll_timeout(ddc->regs + DDC_DDCMCTL1, val, |
110 | (val & DDCM_TRI) != DDCM_TRI, 4, 20000); |
111 | } |
112 | |
113 | static int mtk_hdmi_ddc_read_msg(struct mtk_hdmi_ddc *ddc, struct i2c_msg *msg) |
114 | { |
115 | struct device *dev = ddc->adap.dev.parent; |
116 | u32 remain_count, ack_count, ack_final, read_count, temp_count; |
117 | u32 index = 0; |
118 | u32 ack; |
119 | int i; |
120 | |
121 | ddcm_trigger_mode(ddc, DDCM_START); |
122 | sif_write_mask(ddc, DDC_DDCMD0, mask: 0xff, shift: 0, val: (msg->addr << 1) | 0x01); |
123 | sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_PGLEN_MASK, DDCM_PGLEN_OFFSET, |
124 | val: 0x00); |
125 | ddcm_trigger_mode(ddc, DDCM_WRITE_DATA); |
126 | ack = sif_read_mask(ddc, DDC_DDCMCTL1, DDCM_ACK_MASK, DDCM_ACK_OFFSET); |
127 | dev_dbg(dev, "ack = 0x%x\n" , ack); |
128 | if (ack != 0x01) { |
129 | dev_err(dev, "i2c ack err!\n" ); |
130 | return -ENXIO; |
131 | } |
132 | |
133 | remain_count = msg->len; |
134 | ack_count = (msg->len - 1) / 8; |
135 | ack_final = 0; |
136 | |
137 | while (remain_count > 0) { |
138 | if (ack_count > 0) { |
139 | read_count = 8; |
140 | ack_final = 0; |
141 | ack_count--; |
142 | } else { |
143 | read_count = remain_count; |
144 | ack_final = 1; |
145 | } |
146 | |
147 | sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_PGLEN_MASK, |
148 | DDCM_PGLEN_OFFSET, val: read_count - 1); |
149 | ddcm_trigger_mode(ddc, mode: (ack_final == 1) ? |
150 | DDCM_READ_DATA_NO_ACK : |
151 | DDCM_READ_DATA_ACK); |
152 | |
153 | ack = sif_read_mask(ddc, DDC_DDCMCTL1, DDCM_ACK_MASK, |
154 | DDCM_ACK_OFFSET); |
155 | temp_count = 0; |
156 | while (((ack & (1 << temp_count)) != 0) && (temp_count < 8)) |
157 | temp_count++; |
158 | if (((ack_final == 1) && (temp_count != (read_count - 1))) || |
159 | ((ack_final == 0) && (temp_count != read_count))) { |
160 | dev_err(dev, "Address NACK! ACK(0x%x)\n" , ack); |
161 | break; |
162 | } |
163 | |
164 | for (i = read_count; i >= 1; i--) { |
165 | int shift; |
166 | int offset; |
167 | |
168 | if (i > 4) { |
169 | offset = DDC_DDCMD1; |
170 | shift = (i - 5) * 8; |
171 | } else { |
172 | offset = DDC_DDCMD0; |
173 | shift = (i - 1) * 8; |
174 | } |
175 | |
176 | msg->buf[index + i - 1] = sif_read_mask(ddc, offset, |
177 | mask: 0xff << shift, |
178 | shift); |
179 | } |
180 | |
181 | remain_count -= read_count; |
182 | index += read_count; |
183 | } |
184 | |
185 | return 0; |
186 | } |
187 | |
188 | static int mtk_hdmi_ddc_write_msg(struct mtk_hdmi_ddc *ddc, struct i2c_msg *msg) |
189 | { |
190 | struct device *dev = ddc->adap.dev.parent; |
191 | u32 ack; |
192 | |
193 | ddcm_trigger_mode(ddc, DDCM_START); |
194 | sif_write_mask(ddc, DDC_DDCMD0, DDCM_DATA0, shift: 0, val: msg->addr << 1); |
195 | sif_write_mask(ddc, DDC_DDCMD0, DDCM_DATA1, shift: 8, val: msg->buf[0]); |
196 | sif_write_mask(ddc, DDC_DDCMCTL1, DDCM_PGLEN_MASK, DDCM_PGLEN_OFFSET, |
197 | val: 0x1); |
198 | ddcm_trigger_mode(ddc, DDCM_WRITE_DATA); |
199 | |
200 | ack = sif_read_mask(ddc, DDC_DDCMCTL1, DDCM_ACK_MASK, DDCM_ACK_OFFSET); |
201 | dev_dbg(dev, "ack = %d\n" , ack); |
202 | |
203 | if (ack != 0x03) { |
204 | dev_err(dev, "i2c ack err!\n" ); |
205 | return -EIO; |
206 | } |
207 | |
208 | return 0; |
209 | } |
210 | |
211 | static int mtk_hdmi_ddc_xfer(struct i2c_adapter *adapter, |
212 | struct i2c_msg *msgs, int num) |
213 | { |
214 | struct mtk_hdmi_ddc *ddc = adapter->algo_data; |
215 | struct device *dev = adapter->dev.parent; |
216 | int ret; |
217 | int i; |
218 | |
219 | if (!ddc) { |
220 | dev_err(dev, "invalid arguments\n" ); |
221 | return -EINVAL; |
222 | } |
223 | |
224 | sif_set_bit(ddc, DDC_DDCMCTL0, DDCM_SCL_STRECH); |
225 | sif_set_bit(ddc, DDC_DDCMCTL0, DDCM_SM0EN); |
226 | sif_clr_bit(ddc, DDC_DDCMCTL0, DDCM_ODRAIN); |
227 | |
228 | if (sif_bit_is_set(ddc, DDC_DDCMCTL1, DDCM_TRI)) { |
229 | dev_err(dev, "ddc line is busy!\n" ); |
230 | return -EBUSY; |
231 | } |
232 | |
233 | sif_write_mask(ddc, DDC_DDCMCTL0, DDCM_CLK_DIV_MASK, |
234 | DDCM_CLK_DIV_OFFSET, SIF1_CLOK); |
235 | |
236 | for (i = 0; i < num; i++) { |
237 | struct i2c_msg *msg = &msgs[i]; |
238 | |
239 | dev_dbg(dev, "i2c msg, adr:0x%x, flags:%d, len :0x%x\n" , |
240 | msg->addr, msg->flags, msg->len); |
241 | |
242 | if (msg->flags & I2C_M_RD) |
243 | ret = mtk_hdmi_ddc_read_msg(ddc, msg); |
244 | else |
245 | ret = mtk_hdmi_ddc_write_msg(ddc, msg); |
246 | if (ret < 0) |
247 | goto xfer_end; |
248 | } |
249 | |
250 | ddcm_trigger_mode(ddc, DDCM_STOP); |
251 | |
252 | return i; |
253 | |
254 | xfer_end: |
255 | ddcm_trigger_mode(ddc, DDCM_STOP); |
256 | dev_err(dev, "ddc failed!\n" ); |
257 | return ret; |
258 | } |
259 | |
260 | static u32 mtk_hdmi_ddc_func(struct i2c_adapter *adapter) |
261 | { |
262 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; |
263 | } |
264 | |
265 | static const struct i2c_algorithm mtk_hdmi_ddc_algorithm = { |
266 | .master_xfer = mtk_hdmi_ddc_xfer, |
267 | .functionality = mtk_hdmi_ddc_func, |
268 | }; |
269 | |
270 | static int mtk_hdmi_ddc_probe(struct platform_device *pdev) |
271 | { |
272 | struct device *dev = &pdev->dev; |
273 | struct mtk_hdmi_ddc *ddc; |
274 | struct resource *mem; |
275 | int ret; |
276 | |
277 | ddc = devm_kzalloc(dev, size: sizeof(struct mtk_hdmi_ddc), GFP_KERNEL); |
278 | if (!ddc) |
279 | return -ENOMEM; |
280 | |
281 | ddc->clk = devm_clk_get(dev, id: "ddc-i2c" ); |
282 | if (IS_ERR(ptr: ddc->clk)) { |
283 | dev_err(dev, "get ddc_clk failed: %p ,\n" , ddc->clk); |
284 | return PTR_ERR(ptr: ddc->clk); |
285 | } |
286 | |
287 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
288 | ddc->regs = devm_ioremap_resource(dev: &pdev->dev, res: mem); |
289 | if (IS_ERR(ptr: ddc->regs)) |
290 | return PTR_ERR(ptr: ddc->regs); |
291 | |
292 | ret = clk_prepare_enable(clk: ddc->clk); |
293 | if (ret) { |
294 | dev_err(dev, "enable ddc clk failed!\n" ); |
295 | return ret; |
296 | } |
297 | |
298 | strscpy(ddc->adap.name, "mediatek-hdmi-ddc" , sizeof(ddc->adap.name)); |
299 | ddc->adap.owner = THIS_MODULE; |
300 | ddc->adap.algo = &mtk_hdmi_ddc_algorithm; |
301 | ddc->adap.retries = 3; |
302 | ddc->adap.dev.of_node = dev->of_node; |
303 | ddc->adap.algo_data = ddc; |
304 | ddc->adap.dev.parent = &pdev->dev; |
305 | |
306 | ret = i2c_add_adapter(adap: &ddc->adap); |
307 | if (ret < 0) { |
308 | dev_err(dev, "failed to add bus to i2c core\n" ); |
309 | goto err_clk_disable; |
310 | } |
311 | |
312 | platform_set_drvdata(pdev, data: ddc); |
313 | |
314 | dev_dbg(dev, "ddc->adap: %p\n" , &ddc->adap); |
315 | dev_dbg(dev, "ddc->clk: %p\n" , ddc->clk); |
316 | dev_dbg(dev, "physical adr: %pa, end: %pa\n" , &mem->start, |
317 | &mem->end); |
318 | |
319 | return 0; |
320 | |
321 | err_clk_disable: |
322 | clk_disable_unprepare(clk: ddc->clk); |
323 | return ret; |
324 | } |
325 | |
326 | static void mtk_hdmi_ddc_remove(struct platform_device *pdev) |
327 | { |
328 | struct mtk_hdmi_ddc *ddc = platform_get_drvdata(pdev); |
329 | |
330 | i2c_del_adapter(adap: &ddc->adap); |
331 | clk_disable_unprepare(clk: ddc->clk); |
332 | } |
333 | |
334 | static const struct of_device_id mtk_hdmi_ddc_match[] = { |
335 | { .compatible = "mediatek,mt8173-hdmi-ddc" , }, |
336 | {}, |
337 | }; |
338 | MODULE_DEVICE_TABLE(of, mtk_hdmi_ddc_match); |
339 | |
340 | struct platform_driver mtk_hdmi_ddc_driver = { |
341 | .probe = mtk_hdmi_ddc_probe, |
342 | .remove_new = mtk_hdmi_ddc_remove, |
343 | .driver = { |
344 | .name = "mediatek-hdmi-ddc" , |
345 | .of_match_table = mtk_hdmi_ddc_match, |
346 | }, |
347 | }; |
348 | |
349 | MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>" ); |
350 | MODULE_DESCRIPTION("MediaTek HDMI DDC Driver" ); |
351 | MODULE_LICENSE("GPL v2" ); |
352 | |