Commit | Line | Data |
---|---|---|
3b1b9750 HV |
1 | /* |
2 | * adv7511_cec.c - Analog Devices ADV7511/33 cec driver | |
3 | * | |
4 | * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. | |
5 | * | |
6 | * This program is free software; you may redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; version 2 of the License. | |
9 | * | |
10 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
11 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
12 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
13 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
14 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
15 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
16 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
17 | * SOFTWARE. | |
18 | * | |
19 | */ | |
20 | ||
21 | #include <linux/device.h> | |
22 | #include <linux/module.h> | |
23 | #include <linux/of_device.h> | |
24 | #include <linux/slab.h> | |
25 | #include <linux/clk.h> | |
26 | ||
27 | #include <media/cec.h> | |
28 | ||
29 | #include "adv7511.h" | |
30 | ||
31 | #define ADV7511_INT1_CEC_MASK \ | |
32 | (ADV7511_INT1_CEC_TX_READY | ADV7511_INT1_CEC_TX_ARBIT_LOST | \ | |
33 | ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1) | |
34 | ||
35 | static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status) | |
36 | { | |
37 | unsigned int offset = adv7511->type == ADV7533 ? | |
38 | ADV7533_REG_CEC_OFFSET : 0; | |
39 | unsigned int val; | |
40 | ||
41 | if (regmap_read(adv7511->regmap_cec, | |
42 | ADV7511_REG_CEC_TX_ENABLE + offset, &val)) | |
43 | return; | |
44 | ||
45 | if ((val & 0x01) == 0) | |
46 | return; | |
47 | ||
48 | if (tx_raw_status & ADV7511_INT1_CEC_TX_ARBIT_LOST) { | |
49 | cec_transmit_attempt_done(adv7511->cec_adap, | |
50 | CEC_TX_STATUS_ARB_LOST); | |
51 | return; | |
52 | } | |
53 | if (tx_raw_status & ADV7511_INT1_CEC_TX_RETRY_TIMEOUT) { | |
54 | u8 status; | |
55 | u8 err_cnt = 0; | |
56 | u8 nack_cnt = 0; | |
57 | u8 low_drive_cnt = 0; | |
58 | unsigned int cnt; | |
59 | ||
60 | /* | |
61 | * We set this status bit since this hardware performs | |
62 | * retransmissions. | |
63 | */ | |
64 | status = CEC_TX_STATUS_MAX_RETRIES; | |
65 | if (regmap_read(adv7511->regmap_cec, | |
66 | ADV7511_REG_CEC_TX_LOW_DRV_CNT + offset, &cnt)) { | |
67 | err_cnt = 1; | |
68 | status |= CEC_TX_STATUS_ERROR; | |
69 | } else { | |
70 | nack_cnt = cnt & 0xf; | |
71 | if (nack_cnt) | |
72 | status |= CEC_TX_STATUS_NACK; | |
73 | low_drive_cnt = cnt >> 4; | |
74 | if (low_drive_cnt) | |
75 | status |= CEC_TX_STATUS_LOW_DRIVE; | |
76 | } | |
77 | cec_transmit_done(adv7511->cec_adap, status, | |
78 | 0, nack_cnt, low_drive_cnt, err_cnt); | |
79 | return; | |
80 | } | |
81 | if (tx_raw_status & ADV7511_INT1_CEC_TX_READY) { | |
82 | cec_transmit_attempt_done(adv7511->cec_adap, CEC_TX_STATUS_OK); | |
83 | return; | |
84 | } | |
85 | } | |
86 | ||
87 | void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1) | |
88 | { | |
89 | unsigned int offset = adv7511->type == ADV7533 ? | |
90 | ADV7533_REG_CEC_OFFSET : 0; | |
91 | const u32 irq_tx_mask = ADV7511_INT1_CEC_TX_READY | | |
92 | ADV7511_INT1_CEC_TX_ARBIT_LOST | | |
93 | ADV7511_INT1_CEC_TX_RETRY_TIMEOUT; | |
94 | struct cec_msg msg = {}; | |
95 | unsigned int len; | |
96 | unsigned int val; | |
97 | u8 i; | |
98 | ||
99 | if (irq1 & irq_tx_mask) | |
100 | adv_cec_tx_raw_status(adv7511, irq1); | |
101 | ||
102 | if (!(irq1 & ADV7511_INT1_CEC_RX_READY1)) | |
103 | return; | |
104 | ||
105 | if (regmap_read(adv7511->regmap_cec, | |
106 | ADV7511_REG_CEC_RX_FRAME_LEN + offset, &len)) | |
107 | return; | |
108 | ||
109 | msg.len = len & 0x1f; | |
110 | ||
111 | if (msg.len > 16) | |
112 | msg.len = 16; | |
113 | ||
114 | if (!msg.len) | |
115 | return; | |
116 | ||
117 | for (i = 0; i < msg.len; i++) { | |
118 | regmap_read(adv7511->regmap_cec, | |
119 | i + ADV7511_REG_CEC_RX_FRAME_HDR + offset, &val); | |
120 | msg.msg[i] = val; | |
121 | } | |
122 | ||
123 | /* toggle to re-enable rx 1 */ | |
124 | regmap_write(adv7511->regmap_cec, | |
125 | ADV7511_REG_CEC_RX_BUFFERS + offset, 1); | |
126 | regmap_write(adv7511->regmap_cec, | |
127 | ADV7511_REG_CEC_RX_BUFFERS + offset, 0); | |
128 | cec_received_msg(adv7511->cec_adap, &msg); | |
129 | } | |
130 | ||
131 | static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable) | |
132 | { | |
133 | struct adv7511 *adv7511 = cec_get_drvdata(adap); | |
134 | unsigned int offset = adv7511->type == ADV7533 ? | |
135 | ADV7533_REG_CEC_OFFSET : 0; | |
136 | ||
137 | if (adv7511->i2c_cec == NULL) | |
138 | return -EIO; | |
139 | ||
140 | if (!adv7511->cec_enabled_adap && enable) { | |
141 | /* power up cec section */ | |
142 | regmap_update_bits(adv7511->regmap_cec, | |
143 | ADV7511_REG_CEC_CLK_DIV + offset, | |
144 | 0x03, 0x01); | |
145 | /* legacy mode and clear all rx buffers */ | |
146 | regmap_write(adv7511->regmap_cec, | |
147 | ADV7511_REG_CEC_RX_BUFFERS + offset, 0x07); | |
148 | regmap_write(adv7511->regmap_cec, | |
149 | ADV7511_REG_CEC_RX_BUFFERS + offset, 0); | |
150 | /* initially disable tx */ | |
151 | regmap_update_bits(adv7511->regmap_cec, | |
152 | ADV7511_REG_CEC_TX_ENABLE + offset, 1, 0); | |
153 | /* enabled irqs: */ | |
154 | /* tx: ready */ | |
155 | /* tx: arbitration lost */ | |
156 | /* tx: retry timeout */ | |
157 | /* rx: ready 1 */ | |
158 | regmap_update_bits(adv7511->regmap, | |
159 | ADV7511_REG_INT_ENABLE(1), 0x3f, | |
160 | ADV7511_INT1_CEC_MASK); | |
161 | } else if (adv7511->cec_enabled_adap && !enable) { | |
162 | regmap_update_bits(adv7511->regmap, | |
163 | ADV7511_REG_INT_ENABLE(1), 0x3f, 0); | |
164 | /* disable address mask 1-3 */ | |
165 | regmap_update_bits(adv7511->regmap_cec, | |
166 | ADV7511_REG_CEC_LOG_ADDR_MASK + offset, | |
167 | 0x70, 0x00); | |
168 | /* power down cec section */ | |
169 | regmap_update_bits(adv7511->regmap_cec, | |
170 | ADV7511_REG_CEC_CLK_DIV + offset, | |
171 | 0x03, 0x00); | |
172 | adv7511->cec_valid_addrs = 0; | |
173 | } | |
174 | adv7511->cec_enabled_adap = enable; | |
175 | return 0; | |
176 | } | |
177 | ||
178 | static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr) | |
179 | { | |
180 | struct adv7511 *adv7511 = cec_get_drvdata(adap); | |
181 | unsigned int offset = adv7511->type == ADV7533 ? | |
182 | ADV7533_REG_CEC_OFFSET : 0; | |
183 | unsigned int i, free_idx = ADV7511_MAX_ADDRS; | |
184 | ||
185 | if (!adv7511->cec_enabled_adap) | |
186 | return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO; | |
187 | ||
188 | if (addr == CEC_LOG_ADDR_INVALID) { | |
189 | regmap_update_bits(adv7511->regmap_cec, | |
190 | ADV7511_REG_CEC_LOG_ADDR_MASK + offset, | |
191 | 0x70, 0); | |
192 | adv7511->cec_valid_addrs = 0; | |
193 | return 0; | |
194 | } | |
195 | ||
196 | for (i = 0; i < ADV7511_MAX_ADDRS; i++) { | |
197 | bool is_valid = adv7511->cec_valid_addrs & (1 << i); | |
198 | ||
199 | if (free_idx == ADV7511_MAX_ADDRS && !is_valid) | |
200 | free_idx = i; | |
201 | if (is_valid && adv7511->cec_addr[i] == addr) | |
202 | return 0; | |
203 | } | |
204 | if (i == ADV7511_MAX_ADDRS) { | |
205 | i = free_idx; | |
206 | if (i == ADV7511_MAX_ADDRS) | |
207 | return -ENXIO; | |
208 | } | |
209 | adv7511->cec_addr[i] = addr; | |
210 | adv7511->cec_valid_addrs |= 1 << i; | |
211 | ||
212 | switch (i) { | |
213 | case 0: | |
214 | /* enable address mask 0 */ | |
215 | regmap_update_bits(adv7511->regmap_cec, | |
216 | ADV7511_REG_CEC_LOG_ADDR_MASK + offset, | |
217 | 0x10, 0x10); | |
218 | /* set address for mask 0 */ | |
219 | regmap_update_bits(adv7511->regmap_cec, | |
220 | ADV7511_REG_CEC_LOG_ADDR_0_1 + offset, | |
221 | 0x0f, addr); | |
222 | break; | |
223 | case 1: | |
224 | /* enable address mask 1 */ | |
225 | regmap_update_bits(adv7511->regmap_cec, | |
226 | ADV7511_REG_CEC_LOG_ADDR_MASK + offset, | |
227 | 0x20, 0x20); | |
228 | /* set address for mask 1 */ | |
229 | regmap_update_bits(adv7511->regmap_cec, | |
230 | ADV7511_REG_CEC_LOG_ADDR_0_1 + offset, | |
231 | 0xf0, addr << 4); | |
232 | break; | |
233 | case 2: | |
234 | /* enable address mask 2 */ | |
235 | regmap_update_bits(adv7511->regmap_cec, | |
236 | ADV7511_REG_CEC_LOG_ADDR_MASK + offset, | |
237 | 0x40, 0x40); | |
238 | /* set address for mask 1 */ | |
239 | regmap_update_bits(adv7511->regmap_cec, | |
240 | ADV7511_REG_CEC_LOG_ADDR_2 + offset, | |
241 | 0x0f, addr); | |
242 | break; | |
243 | } | |
244 | return 0; | |
245 | } | |
246 | ||
247 | static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, | |
248 | u32 signal_free_time, struct cec_msg *msg) | |
249 | { | |
250 | struct adv7511 *adv7511 = cec_get_drvdata(adap); | |
251 | unsigned int offset = adv7511->type == ADV7533 ? | |
252 | ADV7533_REG_CEC_OFFSET : 0; | |
253 | u8 len = msg->len; | |
254 | unsigned int i; | |
255 | ||
256 | /* | |
257 | * The number of retries is the number of attempts - 1, but retry | |
258 | * at least once. It's not clear if a value of 0 is allowed, so | |
259 | * let's do at least one retry. | |
260 | */ | |
261 | regmap_update_bits(adv7511->regmap_cec, | |
262 | ADV7511_REG_CEC_TX_RETRY + offset, | |
263 | 0x70, max(1, attempts - 1) << 4); | |
264 | ||
265 | /* blocking, clear cec tx irq status */ | |
266 | regmap_update_bits(adv7511->regmap, ADV7511_REG_INT(1), 0x38, 0x38); | |
267 | ||
268 | /* write data */ | |
269 | for (i = 0; i < len; i++) | |
270 | regmap_write(adv7511->regmap_cec, | |
271 | i + ADV7511_REG_CEC_TX_FRAME_HDR + offset, | |
272 | msg->msg[i]); | |
273 | ||
274 | /* set length (data + header) */ | |
275 | regmap_write(adv7511->regmap_cec, | |
276 | ADV7511_REG_CEC_TX_FRAME_LEN + offset, len); | |
277 | /* start transmit, enable tx */ | |
278 | regmap_write(adv7511->regmap_cec, | |
279 | ADV7511_REG_CEC_TX_ENABLE + offset, 0x01); | |
280 | return 0; | |
281 | } | |
282 | ||
283 | static const struct cec_adap_ops adv7511_cec_adap_ops = { | |
284 | .adap_enable = adv7511_cec_adap_enable, | |
285 | .adap_log_addr = adv7511_cec_adap_log_addr, | |
286 | .adap_transmit = adv7511_cec_adap_transmit, | |
287 | }; | |
288 | ||
289 | static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511) | |
290 | { | |
291 | adv7511->cec_clk = devm_clk_get(dev, "cec"); | |
292 | if (IS_ERR(adv7511->cec_clk)) { | |
293 | int ret = PTR_ERR(adv7511->cec_clk); | |
294 | ||
295 | adv7511->cec_clk = NULL; | |
296 | return ret; | |
297 | } | |
298 | clk_prepare_enable(adv7511->cec_clk); | |
299 | adv7511->cec_clk_freq = clk_get_rate(adv7511->cec_clk); | |
300 | return 0; | |
301 | } | |
302 | ||
1b6fba45 | 303 | int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511) |
3b1b9750 | 304 | { |
1b6fba45 HV |
305 | unsigned int offset = adv7511->type == ADV7533 ? |
306 | ADV7533_REG_CEC_OFFSET : 0; | |
3b1b9750 HV |
307 | int ret = adv7511_cec_parse_dt(dev, adv7511); |
308 | ||
309 | if (ret) | |
1b6fba45 | 310 | goto err_cec_parse_dt; |
3b1b9750 HV |
311 | |
312 | adv7511->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops, | |
313 | adv7511, dev_name(dev), CEC_CAP_DEFAULTS, ADV7511_MAX_ADDRS); | |
1b6fba45 HV |
314 | if (IS_ERR(adv7511->cec_adap)) { |
315 | ret = PTR_ERR(adv7511->cec_adap); | |
316 | goto err_cec_alloc; | |
317 | } | |
3b1b9750 HV |
318 | |
319 | regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset, 0); | |
320 | /* cec soft reset */ | |
321 | regmap_write(adv7511->regmap_cec, | |
322 | ADV7511_REG_CEC_SOFT_RESET + offset, 0x01); | |
323 | regmap_write(adv7511->regmap_cec, | |
324 | ADV7511_REG_CEC_SOFT_RESET + offset, 0x00); | |
325 | ||
326 | /* legacy mode */ | |
327 | regmap_write(adv7511->regmap_cec, | |
328 | ADV7511_REG_CEC_RX_BUFFERS + offset, 0x00); | |
329 | ||
330 | regmap_write(adv7511->regmap_cec, | |
331 | ADV7511_REG_CEC_CLK_DIV + offset, | |
332 | ((adv7511->cec_clk_freq / 750000) - 1) << 2); | |
333 | ||
334 | ret = cec_register_adapter(adv7511->cec_adap, dev); | |
1b6fba45 HV |
335 | if (ret) |
336 | goto err_cec_register; | |
337 | return 0; | |
338 | ||
339 | err_cec_register: | |
340 | cec_delete_adapter(adv7511->cec_adap); | |
341 | adv7511->cec_adap = NULL; | |
342 | err_cec_alloc: | |
343 | dev_info(dev, "Initializing CEC failed with error %d, disabling CEC\n", | |
344 | ret); | |
345 | err_cec_parse_dt: | |
346 | regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL + offset, | |
347 | ADV7511_CEC_CTRL_POWER_DOWN); | |
348 | return ret == -EPROBE_DEFER ? ret : 0; | |
3b1b9750 | 349 | } |