Linux Audio

Check our new training course

Yocto / OpenEmbedded training

Mar 24-27, 2025, special US time zones
Register
Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * adv7511_cec.c - Analog Devices ADV7511/33 cec driver
  4 *
  5 * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  6 */
  7
  8#include <linux/device.h>
  9#include <linux/module.h>
 10#include <linux/slab.h>
 11#include <linux/clk.h>
 12
 13#include <media/cec.h>
 14
 15#include "adv7511.h"
 16
 17static const u8 ADV7511_REG_CEC_RX_FRAME_HDR[] = {
 18	ADV7511_REG_CEC_RX1_FRAME_HDR,
 19	ADV7511_REG_CEC_RX2_FRAME_HDR,
 20	ADV7511_REG_CEC_RX3_FRAME_HDR,
 21};
 22
 23static const u8 ADV7511_REG_CEC_RX_FRAME_LEN[] = {
 24	ADV7511_REG_CEC_RX1_FRAME_LEN,
 25	ADV7511_REG_CEC_RX2_FRAME_LEN,
 26	ADV7511_REG_CEC_RX3_FRAME_LEN,
 27};
 28
 29#define ADV7511_INT1_CEC_MASK \
 30	(ADV7511_INT1_CEC_TX_READY | ADV7511_INT1_CEC_TX_ARBIT_LOST | \
 31	 ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1 | \
 32	 ADV7511_INT1_CEC_RX_READY2 | ADV7511_INT1_CEC_RX_READY3)
 33
 34static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status)
 35{
 36	unsigned int offset = adv7511->info->reg_cec_offset;
 37	unsigned int val;
 38
 39	if (regmap_read(adv7511->regmap_cec,
 40			ADV7511_REG_CEC_TX_ENABLE + offset, &val))
 41		return;
 42
 43	if ((val & 0x01) == 0)
 44		return;
 45
 46	if (tx_raw_status & ADV7511_INT1_CEC_TX_ARBIT_LOST) {
 47		cec_transmit_attempt_done(adv7511->cec_adap,
 48					  CEC_TX_STATUS_ARB_LOST);
 49		return;
 50	}
 51	if (tx_raw_status & ADV7511_INT1_CEC_TX_RETRY_TIMEOUT) {
 52		u8 status;
 53		u8 err_cnt = 0;
 54		u8 nack_cnt = 0;
 55		u8 low_drive_cnt = 0;
 56		unsigned int cnt;
 57
 58		/*
 59		 * We set this status bit since this hardware performs
 60		 * retransmissions.
 61		 */
 62		status = CEC_TX_STATUS_MAX_RETRIES;
 63		if (regmap_read(adv7511->regmap_cec,
 64			    ADV7511_REG_CEC_TX_LOW_DRV_CNT + offset, &cnt)) {
 65			err_cnt = 1;
 66			status |= CEC_TX_STATUS_ERROR;
 67		} else {
 68			nack_cnt = cnt & 0xf;
 69			if (nack_cnt)
 70				status |= CEC_TX_STATUS_NACK;
 71			low_drive_cnt = cnt >> 4;
 72			if (low_drive_cnt)
 73				status |= CEC_TX_STATUS_LOW_DRIVE;
 74		}
 75		cec_transmit_done(adv7511->cec_adap, status,
 76				  0, nack_cnt, low_drive_cnt, err_cnt);
 77		return;
 78	}
 79	if (tx_raw_status & ADV7511_INT1_CEC_TX_READY) {
 80		cec_transmit_attempt_done(adv7511->cec_adap, CEC_TX_STATUS_OK);
 81		return;
 82	}
 83}
 84
 85static void adv7511_cec_rx(struct adv7511 *adv7511, int rx_buf)
 86{
 87	unsigned int offset = adv7511->info->reg_cec_offset;
 88	struct cec_msg msg = {};
 89	unsigned int len;
 90	unsigned int val;
 91	u8 i;
 92
 93	if (regmap_read(adv7511->regmap_cec,
 94			ADV7511_REG_CEC_RX_FRAME_LEN[rx_buf] + offset, &len))
 95		return;
 96
 97	msg.len = len & 0x1f;
 98
 99	if (msg.len > 16)
100		msg.len = 16;
101
102	if (!msg.len)
103		return;
104
105	for (i = 0; i < msg.len; i++) {
106		regmap_read(adv7511->regmap_cec,
107			    i + ADV7511_REG_CEC_RX_FRAME_HDR[rx_buf] + offset,
108			    &val);
109		msg.msg[i] = val;
110	}
111
112	/* Toggle RX Ready Clear bit to re-enable this RX buffer */
113	regmap_update_bits(adv7511->regmap_cec,
114			   ADV7511_REG_CEC_RX_BUFFERS + offset, BIT(rx_buf),
115			   BIT(rx_buf));
116	regmap_update_bits(adv7511->regmap_cec,
117			   ADV7511_REG_CEC_RX_BUFFERS + offset, BIT(rx_buf), 0);
118
119	cec_received_msg(adv7511->cec_adap, &msg);
120}
121
122void adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
123{
124	unsigned int offset = adv7511->info->reg_cec_offset;
125	const u32 irq_tx_mask = ADV7511_INT1_CEC_TX_READY |
126				ADV7511_INT1_CEC_TX_ARBIT_LOST |
127				ADV7511_INT1_CEC_TX_RETRY_TIMEOUT;
128	const u32 irq_rx_mask = ADV7511_INT1_CEC_RX_READY1 |
129				ADV7511_INT1_CEC_RX_READY2 |
130				ADV7511_INT1_CEC_RX_READY3;
131	unsigned int rx_status;
132	int rx_order[3] = { -1, -1, -1 };
133	int i;
134
135	if (irq1 & irq_tx_mask)
136		adv_cec_tx_raw_status(adv7511, irq1);
137
138	if (!(irq1 & irq_rx_mask))
139		return;
140
141	if (regmap_read(adv7511->regmap_cec,
142			ADV7511_REG_CEC_RX_STATUS + offset, &rx_status))
143		return;
144
145	/*
146	 * ADV7511_REG_CEC_RX_STATUS[5:0] contains the reception order of RX
147	 * buffers 0, 1, and 2 in bits [1:0], [3:2], and [5:4] respectively.
148	 * The values are to be interpreted as follows:
149	 *
150	 *   0 = buffer unused
151	 *   1 = buffer contains oldest received frame (if applicable)
152	 *   2 = buffer contains second oldest received frame (if applicable)
153	 *   3 = buffer contains third oldest received frame (if applicable)
154	 *
155	 * Fill rx_order with the sequence of RX buffer indices to
156	 * read from in order, where -1 indicates that there are no
157	 * more buffers to process.
158	 */
159	for (i = 0; i < 3; i++) {
160		unsigned int timestamp = (rx_status >> (2 * i)) & 0x3;
161
162		if (timestamp)
163			rx_order[timestamp - 1] = i;
164	}
165
166	/* Read CEC RX buffers in the appropriate order as prescribed above */
167	for (i = 0; i < 3; i++) {
168		int rx_buf = rx_order[i];
169
170		if (rx_buf < 0)
171			break;
172
173		adv7511_cec_rx(adv7511, rx_buf);
174	}
175}
176
177static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
178{
179	struct adv7511 *adv7511 = cec_get_drvdata(adap);
180	unsigned int offset = adv7511->info->reg_cec_offset;
181
182	if (adv7511->i2c_cec == NULL)
183		return -EIO;
184
185	if (!adv7511->cec_enabled_adap && enable) {
186		/* power up cec section */
187		regmap_update_bits(adv7511->regmap_cec,
188				   ADV7511_REG_CEC_CLK_DIV + offset,
189				   0x03, 0x01);
190		/* non-legacy mode and clear all rx buffers */
191		regmap_write(adv7511->regmap_cec,
192			     ADV7511_REG_CEC_RX_BUFFERS + offset, 0x0f);
193		regmap_write(adv7511->regmap_cec,
194			     ADV7511_REG_CEC_RX_BUFFERS + offset, 0x08);
195		/* initially disable tx */
196		regmap_update_bits(adv7511->regmap_cec,
197				   ADV7511_REG_CEC_TX_ENABLE + offset, 1, 0);
198		/* enabled irqs: */
199		/* tx: ready */
200		/* tx: arbitration lost */
201		/* tx: retry timeout */
202		/* rx: ready 1-3 */
203		regmap_update_bits(adv7511->regmap,
204				   ADV7511_REG_INT_ENABLE(1), 0x3f,
205				   ADV7511_INT1_CEC_MASK);
206	} else if (adv7511->cec_enabled_adap && !enable) {
207		regmap_update_bits(adv7511->regmap,
208				   ADV7511_REG_INT_ENABLE(1), 0x3f, 0);
209		/* disable address mask 1-3 */
210		regmap_update_bits(adv7511->regmap_cec,
211				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
212				   0x70, 0x00);
213		/* power down cec section */
214		regmap_update_bits(adv7511->regmap_cec,
215				   ADV7511_REG_CEC_CLK_DIV + offset,
216				   0x03, 0x00);
217		adv7511->cec_valid_addrs = 0;
218	}
219	adv7511->cec_enabled_adap = enable;
220	return 0;
221}
222
223static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
224{
225	struct adv7511 *adv7511 = cec_get_drvdata(adap);
226	unsigned int offset = adv7511->info->reg_cec_offset;
227	unsigned int i, free_idx = ADV7511_MAX_ADDRS;
228
229	if (!adv7511->cec_enabled_adap)
230		return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
231
232	if (addr == CEC_LOG_ADDR_INVALID) {
233		regmap_update_bits(adv7511->regmap_cec,
234				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
235				   0x70, 0);
236		adv7511->cec_valid_addrs = 0;
237		return 0;
238	}
239
240	for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
241		bool is_valid = adv7511->cec_valid_addrs & (1 << i);
242
243		if (free_idx == ADV7511_MAX_ADDRS && !is_valid)
244			free_idx = i;
245		if (is_valid && adv7511->cec_addr[i] == addr)
246			return 0;
247	}
248	if (i == ADV7511_MAX_ADDRS) {
249		i = free_idx;
250		if (i == ADV7511_MAX_ADDRS)
251			return -ENXIO;
252	}
253	adv7511->cec_addr[i] = addr;
254	adv7511->cec_valid_addrs |= 1 << i;
255
256	switch (i) {
257	case 0:
258		/* enable address mask 0 */
259		regmap_update_bits(adv7511->regmap_cec,
260				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
261				   0x10, 0x10);
262		/* set address for mask 0 */
263		regmap_update_bits(adv7511->regmap_cec,
264				   ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
265				   0x0f, addr);
266		break;
267	case 1:
268		/* enable address mask 1 */
269		regmap_update_bits(adv7511->regmap_cec,
270				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
271				   0x20, 0x20);
272		/* set address for mask 1 */
273		regmap_update_bits(adv7511->regmap_cec,
274				   ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
275				   0xf0, addr << 4);
276		break;
277	case 2:
278		/* enable address mask 2 */
279		regmap_update_bits(adv7511->regmap_cec,
280				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
281				   0x40, 0x40);
282		/* set address for mask 1 */
283		regmap_update_bits(adv7511->regmap_cec,
284				   ADV7511_REG_CEC_LOG_ADDR_2 + offset,
285				   0x0f, addr);
286		break;
287	}
288	return 0;
289}
290
291static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
292				     u32 signal_free_time, struct cec_msg *msg)
293{
294	struct adv7511 *adv7511 = cec_get_drvdata(adap);
295	unsigned int offset = adv7511->info->reg_cec_offset;
296	u8 len = msg->len;
297	unsigned int i;
298
299	/*
300	 * The number of retries is the number of attempts - 1, but retry
301	 * at least once. It's not clear if a value of 0 is allowed, so
302	 * let's do at least one retry.
303	 */
304	regmap_update_bits(adv7511->regmap_cec,
305			   ADV7511_REG_CEC_TX_RETRY + offset,
306			   0x70, max(1, attempts - 1) << 4);
307
308	/* blocking, clear cec tx irq status */
309	regmap_update_bits(adv7511->regmap, ADV7511_REG_INT(1), 0x38, 0x38);
310
311	/* write data */
312	for (i = 0; i < len; i++)
313		regmap_write(adv7511->regmap_cec,
314			     i + ADV7511_REG_CEC_TX_FRAME_HDR + offset,
315			     msg->msg[i]);
316
317	/* set length (data + header) */
318	regmap_write(adv7511->regmap_cec,
319		     ADV7511_REG_CEC_TX_FRAME_LEN + offset, len);
320	/* start transmit, enable tx */
321	regmap_write(adv7511->regmap_cec,
322		     ADV7511_REG_CEC_TX_ENABLE + offset, 0x01);
323	return 0;
324}
325
326static const struct cec_adap_ops adv7511_cec_adap_ops = {
327	.adap_enable = adv7511_cec_adap_enable,
328	.adap_log_addr = adv7511_cec_adap_log_addr,
329	.adap_transmit = adv7511_cec_adap_transmit,
330};
331
332static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511)
333{
334	adv7511->cec_clk = devm_clk_get(dev, "cec");
335	if (IS_ERR(adv7511->cec_clk)) {
336		int ret = PTR_ERR(adv7511->cec_clk);
337
338		adv7511->cec_clk = NULL;
339		return ret;
340	}
341	clk_prepare_enable(adv7511->cec_clk);
342	adv7511->cec_clk_freq = clk_get_rate(adv7511->cec_clk);
343	return 0;
344}
345
346int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511)
347{
348	unsigned int offset = adv7511->info->reg_cec_offset;
349	int ret = adv7511_cec_parse_dt(dev, adv7511);
350
351	if (ret)
352		goto err_cec_parse_dt;
353
354	adv7511->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
355		adv7511, dev_name(dev), CEC_CAP_DEFAULTS, ADV7511_MAX_ADDRS);
356	if (IS_ERR(adv7511->cec_adap)) {
357		ret = PTR_ERR(adv7511->cec_adap);
358		goto err_cec_alloc;
359	}
360
361	regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, 0);
362	/* cec soft reset */
363	regmap_write(adv7511->regmap_cec,
364		     ADV7511_REG_CEC_SOFT_RESET + offset, 0x01);
365	regmap_write(adv7511->regmap_cec,
366		     ADV7511_REG_CEC_SOFT_RESET + offset, 0x00);
367
368	/* non-legacy mode - use all three RX buffers */
369	regmap_write(adv7511->regmap_cec,
370		     ADV7511_REG_CEC_RX_BUFFERS + offset, 0x08);
371
372	regmap_write(adv7511->regmap_cec,
373		     ADV7511_REG_CEC_CLK_DIV + offset,
374		     ((adv7511->cec_clk_freq / 750000) - 1) << 2);
375
376	ret = cec_register_adapter(adv7511->cec_adap, dev);
377	if (ret)
378		goto err_cec_register;
379	return 0;
380
381err_cec_register:
382	cec_delete_adapter(adv7511->cec_adap);
383	adv7511->cec_adap = NULL;
384err_cec_alloc:
385	dev_info(dev, "Initializing CEC failed with error %d, disabling CEC\n",
386		 ret);
387err_cec_parse_dt:
388	regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL,
389		     ADV7511_CEC_CTRL_POWER_DOWN);
390	return ret == -EPROBE_DEFER ? ret : 0;
391}