14c216f0dSCai Huoqing // SPDX-License-Identifier: GPL-2.0-only
23b1b9750SHans Verkuil /*
33b1b9750SHans Verkuil  * adv7511_cec.c - Analog Devices ADV7511/33 cec driver
43b1b9750SHans Verkuil  *
53b1b9750SHans Verkuil  * Copyright 2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
63b1b9750SHans Verkuil  */
73b1b9750SHans Verkuil 
83b1b9750SHans Verkuil #include <linux/device.h>
93b1b9750SHans Verkuil #include <linux/module.h>
103b1b9750SHans Verkuil #include <linux/slab.h>
113b1b9750SHans Verkuil #include <linux/clk.h>
123b1b9750SHans Verkuil 
133b1b9750SHans Verkuil #include <media/cec.h>
143b1b9750SHans Verkuil 
153b1b9750SHans Verkuil #include "adv7511.h"
163b1b9750SHans Verkuil 
1791776af1SFabio Estevam static const u8 ADV7511_REG_CEC_RX_FRAME_HDR[] = {
1891776af1SFabio Estevam 	ADV7511_REG_CEC_RX1_FRAME_HDR,
1991776af1SFabio Estevam 	ADV7511_REG_CEC_RX2_FRAME_HDR,
2091776af1SFabio Estevam 	ADV7511_REG_CEC_RX3_FRAME_HDR,
2191776af1SFabio Estevam };
2291776af1SFabio Estevam 
2391776af1SFabio Estevam static const u8 ADV7511_REG_CEC_RX_FRAME_LEN[] = {
2491776af1SFabio Estevam 	ADV7511_REG_CEC_RX1_FRAME_LEN,
2591776af1SFabio Estevam 	ADV7511_REG_CEC_RX2_FRAME_LEN,
2691776af1SFabio Estevam 	ADV7511_REG_CEC_RX3_FRAME_LEN,
2791776af1SFabio Estevam };
2891776af1SFabio Estevam 
293b1b9750SHans Verkuil #define ADV7511_INT1_CEC_MASK \
303b1b9750SHans Verkuil 	(ADV7511_INT1_CEC_TX_READY | ADV7511_INT1_CEC_TX_ARBIT_LOST | \
31ab0af093SAlvin Šipraga 	 ADV7511_INT1_CEC_TX_RETRY_TIMEOUT | ADV7511_INT1_CEC_RX_READY1 | \
32ab0af093SAlvin Šipraga 	 ADV7511_INT1_CEC_RX_READY2 | ADV7511_INT1_CEC_RX_READY3)
333b1b9750SHans Verkuil 
adv_cec_tx_raw_status(struct adv7511 * adv7511,u8 tx_raw_status)343b1b9750SHans Verkuil static void adv_cec_tx_raw_status(struct adv7511 *adv7511, u8 tx_raw_status)
353b1b9750SHans Verkuil {
368d6cf571SBiju Das 	unsigned int offset = adv7511->info->reg_cec_offset;
373b1b9750SHans Verkuil 	unsigned int val;
383b1b9750SHans Verkuil 
393b1b9750SHans Verkuil 	if (regmap_read(adv7511->regmap_cec,
403b1b9750SHans Verkuil 			ADV7511_REG_CEC_TX_ENABLE + offset, &val))
413b1b9750SHans Verkuil 		return;
423b1b9750SHans Verkuil 
433b1b9750SHans Verkuil 	if ((val & 0x01) == 0)
443b1b9750SHans Verkuil 		return;
453b1b9750SHans Verkuil 
463b1b9750SHans Verkuil 	if (tx_raw_status & ADV7511_INT1_CEC_TX_ARBIT_LOST) {
473b1b9750SHans Verkuil 		cec_transmit_attempt_done(adv7511->cec_adap,
483b1b9750SHans Verkuil 					  CEC_TX_STATUS_ARB_LOST);
493b1b9750SHans Verkuil 		return;
503b1b9750SHans Verkuil 	}
513b1b9750SHans Verkuil 	if (tx_raw_status & ADV7511_INT1_CEC_TX_RETRY_TIMEOUT) {
523b1b9750SHans Verkuil 		u8 status;
533b1b9750SHans Verkuil 		u8 err_cnt = 0;
543b1b9750SHans Verkuil 		u8 nack_cnt = 0;
553b1b9750SHans Verkuil 		u8 low_drive_cnt = 0;
563b1b9750SHans Verkuil 		unsigned int cnt;
573b1b9750SHans Verkuil 
583b1b9750SHans Verkuil 		/*
593b1b9750SHans Verkuil 		 * We set this status bit since this hardware performs
603b1b9750SHans Verkuil 		 * retransmissions.
613b1b9750SHans Verkuil 		 */
623b1b9750SHans Verkuil 		status = CEC_TX_STATUS_MAX_RETRIES;
633b1b9750SHans Verkuil 		if (regmap_read(adv7511->regmap_cec,
643b1b9750SHans Verkuil 			    ADV7511_REG_CEC_TX_LOW_DRV_CNT + offset, &cnt)) {
653b1b9750SHans Verkuil 			err_cnt = 1;
663b1b9750SHans Verkuil 			status |= CEC_TX_STATUS_ERROR;
673b1b9750SHans Verkuil 		} else {
683b1b9750SHans Verkuil 			nack_cnt = cnt & 0xf;
693b1b9750SHans Verkuil 			if (nack_cnt)
703b1b9750SHans Verkuil 				status |= CEC_TX_STATUS_NACK;
713b1b9750SHans Verkuil 			low_drive_cnt = cnt >> 4;
723b1b9750SHans Verkuil 			if (low_drive_cnt)
733b1b9750SHans Verkuil 				status |= CEC_TX_STATUS_LOW_DRIVE;
743b1b9750SHans Verkuil 		}
753b1b9750SHans Verkuil 		cec_transmit_done(adv7511->cec_adap, status,
763b1b9750SHans Verkuil 				  0, nack_cnt, low_drive_cnt, err_cnt);
773b1b9750SHans Verkuil 		return;
783b1b9750SHans Verkuil 	}
793b1b9750SHans Verkuil 	if (tx_raw_status & ADV7511_INT1_CEC_TX_READY) {
803b1b9750SHans Verkuil 		cec_transmit_attempt_done(adv7511->cec_adap, CEC_TX_STATUS_OK);
813b1b9750SHans Verkuil 		return;
823b1b9750SHans Verkuil 	}
833b1b9750SHans Verkuil }
843b1b9750SHans Verkuil 
adv7511_cec_rx(struct adv7511 * adv7511,int rx_buf)85ab0af093SAlvin Šipraga static void adv7511_cec_rx(struct adv7511 *adv7511, int rx_buf)
863b1b9750SHans Verkuil {
878d6cf571SBiju Das 	unsigned int offset = adv7511->info->reg_cec_offset;
883b1b9750SHans Verkuil 	struct cec_msg msg = {};
893b1b9750SHans Verkuil 	unsigned int len;
903b1b9750SHans Verkuil 	unsigned int val;
913b1b9750SHans Verkuil 	u8 i;
923b1b9750SHans Verkuil 
933b1b9750SHans Verkuil 	if (regmap_read(adv7511->regmap_cec,
94ab0af093SAlvin Šipraga 			ADV7511_REG_CEC_RX_FRAME_LEN[rx_buf] + offset, &len))
953b1b9750SHans Verkuil 		return;
963b1b9750SHans Verkuil 
973b1b9750SHans Verkuil 	msg.len = len & 0x1f;
983b1b9750SHans Verkuil 
993b1b9750SHans Verkuil 	if (msg.len > 16)
1003b1b9750SHans Verkuil 		msg.len = 16;
1013b1b9750SHans Verkuil 
1023b1b9750SHans Verkuil 	if (!msg.len)
1033b1b9750SHans Verkuil 		return;
1043b1b9750SHans Verkuil 
1053b1b9750SHans Verkuil 	for (i = 0; i < msg.len; i++) {
1063b1b9750SHans Verkuil 		regmap_read(adv7511->regmap_cec,
107ab0af093SAlvin Šipraga 			    i + ADV7511_REG_CEC_RX_FRAME_HDR[rx_buf] + offset,
108ab0af093SAlvin Šipraga 			    &val);
1093b1b9750SHans Verkuil 		msg.msg[i] = val;
1103b1b9750SHans Verkuil 	}
1113b1b9750SHans Verkuil 
112ab0af093SAlvin Šipraga 	/* Toggle RX Ready Clear bit to re-enable this RX buffer */
113ab0af093SAlvin Šipraga 	regmap_update_bits(adv7511->regmap_cec,
114ab0af093SAlvin Šipraga 			   ADV7511_REG_CEC_RX_BUFFERS + offset, BIT(rx_buf),
115ab0af093SAlvin Šipraga 			   BIT(rx_buf));
116ab0af093SAlvin Šipraga 	regmap_update_bits(adv7511->regmap_cec,
117ab0af093SAlvin Šipraga 			   ADV7511_REG_CEC_RX_BUFFERS + offset, BIT(rx_buf), 0);
118ab0af093SAlvin Šipraga 
1193b1b9750SHans Verkuil 	cec_received_msg(adv7511->cec_adap, &msg);
1203b1b9750SHans Verkuil }
1213b1b9750SHans Verkuil 
adv7511_cec_irq_process(struct adv7511 * adv7511,unsigned int irq1)122*91f9f4a3SAdam Ford int adv7511_cec_irq_process(struct adv7511 *adv7511, unsigned int irq1)
123ab0af093SAlvin Šipraga {
1248d6cf571SBiju Das 	unsigned int offset = adv7511->info->reg_cec_offset;
125ab0af093SAlvin Šipraga 	const u32 irq_tx_mask = ADV7511_INT1_CEC_TX_READY |
126ab0af093SAlvin Šipraga 				ADV7511_INT1_CEC_TX_ARBIT_LOST |
127ab0af093SAlvin Šipraga 				ADV7511_INT1_CEC_TX_RETRY_TIMEOUT;
128ab0af093SAlvin Šipraga 	const u32 irq_rx_mask = ADV7511_INT1_CEC_RX_READY1 |
129ab0af093SAlvin Šipraga 				ADV7511_INT1_CEC_RX_READY2 |
130ab0af093SAlvin Šipraga 				ADV7511_INT1_CEC_RX_READY3;
131ab0af093SAlvin Šipraga 	unsigned int rx_status;
132ab0af093SAlvin Šipraga 	int rx_order[3] = { -1, -1, -1 };
133ab0af093SAlvin Šipraga 	int i;
134*91f9f4a3SAdam Ford 	int irq_status = IRQ_NONE;
135ab0af093SAlvin Šipraga 
136*91f9f4a3SAdam Ford 	if (irq1 & irq_tx_mask) {
137ab0af093SAlvin Šipraga 		adv_cec_tx_raw_status(adv7511, irq1);
138*91f9f4a3SAdam Ford 		irq_status = IRQ_HANDLED;
139*91f9f4a3SAdam Ford 	}
140ab0af093SAlvin Šipraga 
141ab0af093SAlvin Šipraga 	if (!(irq1 & irq_rx_mask))
142*91f9f4a3SAdam Ford 		return irq_status;
143ab0af093SAlvin Šipraga 
144ab0af093SAlvin Šipraga 	if (regmap_read(adv7511->regmap_cec,
145ab0af093SAlvin Šipraga 			ADV7511_REG_CEC_RX_STATUS + offset, &rx_status))
146*91f9f4a3SAdam Ford 		return irq_status;
147ab0af093SAlvin Šipraga 
148ab0af093SAlvin Šipraga 	/*
149ab0af093SAlvin Šipraga 	 * ADV7511_REG_CEC_RX_STATUS[5:0] contains the reception order of RX
150ab0af093SAlvin Šipraga 	 * buffers 0, 1, and 2 in bits [1:0], [3:2], and [5:4] respectively.
151ab0af093SAlvin Šipraga 	 * The values are to be interpreted as follows:
152ab0af093SAlvin Šipraga 	 *
153ab0af093SAlvin Šipraga 	 *   0 = buffer unused
154ab0af093SAlvin Šipraga 	 *   1 = buffer contains oldest received frame (if applicable)
155ab0af093SAlvin Šipraga 	 *   2 = buffer contains second oldest received frame (if applicable)
156ab0af093SAlvin Šipraga 	 *   3 = buffer contains third oldest received frame (if applicable)
157ab0af093SAlvin Šipraga 	 *
158ab0af093SAlvin Šipraga 	 * Fill rx_order with the sequence of RX buffer indices to
159ab0af093SAlvin Šipraga 	 * read from in order, where -1 indicates that there are no
160ab0af093SAlvin Šipraga 	 * more buffers to process.
161ab0af093SAlvin Šipraga 	 */
162ab0af093SAlvin Šipraga 	for (i = 0; i < 3; i++) {
163ab0af093SAlvin Šipraga 		unsigned int timestamp = (rx_status >> (2 * i)) & 0x3;
164ab0af093SAlvin Šipraga 
165ab0af093SAlvin Šipraga 		if (timestamp)
166ab0af093SAlvin Šipraga 			rx_order[timestamp - 1] = i;
167ab0af093SAlvin Šipraga 	}
168ab0af093SAlvin Šipraga 
169ab0af093SAlvin Šipraga 	/* Read CEC RX buffers in the appropriate order as prescribed above */
170ab0af093SAlvin Šipraga 	for (i = 0; i < 3; i++) {
171ab0af093SAlvin Šipraga 		int rx_buf = rx_order[i];
172ab0af093SAlvin Šipraga 
173ab0af093SAlvin Šipraga 		if (rx_buf < 0)
174ab0af093SAlvin Šipraga 			break;
175ab0af093SAlvin Šipraga 
176ab0af093SAlvin Šipraga 		adv7511_cec_rx(adv7511, rx_buf);
177ab0af093SAlvin Šipraga 	}
178*91f9f4a3SAdam Ford 
179*91f9f4a3SAdam Ford 	return IRQ_HANDLED;
180ab0af093SAlvin Šipraga }
181ab0af093SAlvin Šipraga 
adv7511_cec_adap_enable(struct cec_adapter * adap,bool enable)1823b1b9750SHans Verkuil static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)
1833b1b9750SHans Verkuil {
1843b1b9750SHans Verkuil 	struct adv7511 *adv7511 = cec_get_drvdata(adap);
1858d6cf571SBiju Das 	unsigned int offset = adv7511->info->reg_cec_offset;
1863b1b9750SHans Verkuil 
1873b1b9750SHans Verkuil 	if (adv7511->i2c_cec == NULL)
1883b1b9750SHans Verkuil 		return -EIO;
1893b1b9750SHans Verkuil 
1903b1b9750SHans Verkuil 	if (!adv7511->cec_enabled_adap && enable) {
1913b1b9750SHans Verkuil 		/* power up cec section */
1923b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap_cec,
1933b1b9750SHans Verkuil 				   ADV7511_REG_CEC_CLK_DIV + offset,
1943b1b9750SHans Verkuil 				   0x03, 0x01);
195ab0af093SAlvin Šipraga 		/* non-legacy mode and clear all rx buffers */
1963b1b9750SHans Verkuil 		regmap_write(adv7511->regmap_cec,
197ab0af093SAlvin Šipraga 			     ADV7511_REG_CEC_RX_BUFFERS + offset, 0x0f);
1983b1b9750SHans Verkuil 		regmap_write(adv7511->regmap_cec,
199ab0af093SAlvin Šipraga 			     ADV7511_REG_CEC_RX_BUFFERS + offset, 0x08);
2003b1b9750SHans Verkuil 		/* initially disable tx */
2013b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap_cec,
2023b1b9750SHans Verkuil 				   ADV7511_REG_CEC_TX_ENABLE + offset, 1, 0);
2033b1b9750SHans Verkuil 		/* enabled irqs: */
2043b1b9750SHans Verkuil 		/* tx: ready */
2053b1b9750SHans Verkuil 		/* tx: arbitration lost */
2063b1b9750SHans Verkuil 		/* tx: retry timeout */
207ab0af093SAlvin Šipraga 		/* rx: ready 1-3 */
2083b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap,
2093b1b9750SHans Verkuil 				   ADV7511_REG_INT_ENABLE(1), 0x3f,
2103b1b9750SHans Verkuil 				   ADV7511_INT1_CEC_MASK);
2113b1b9750SHans Verkuil 	} else if (adv7511->cec_enabled_adap && !enable) {
2123b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap,
2133b1b9750SHans Verkuil 				   ADV7511_REG_INT_ENABLE(1), 0x3f, 0);
2143b1b9750SHans Verkuil 		/* disable address mask 1-3 */
2153b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap_cec,
2163b1b9750SHans Verkuil 				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
2173b1b9750SHans Verkuil 				   0x70, 0x00);
2183b1b9750SHans Verkuil 		/* power down cec section */
2193b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap_cec,
2203b1b9750SHans Verkuil 				   ADV7511_REG_CEC_CLK_DIV + offset,
2213b1b9750SHans Verkuil 				   0x03, 0x00);
2223b1b9750SHans Verkuil 		adv7511->cec_valid_addrs = 0;
2233b1b9750SHans Verkuil 	}
2243b1b9750SHans Verkuil 	adv7511->cec_enabled_adap = enable;
2253b1b9750SHans Verkuil 	return 0;
2263b1b9750SHans Verkuil }
2273b1b9750SHans Verkuil 
adv7511_cec_adap_log_addr(struct cec_adapter * adap,u8 addr)2283b1b9750SHans Verkuil static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
2293b1b9750SHans Verkuil {
2303b1b9750SHans Verkuil 	struct adv7511 *adv7511 = cec_get_drvdata(adap);
2318d6cf571SBiju Das 	unsigned int offset = adv7511->info->reg_cec_offset;
2323b1b9750SHans Verkuil 	unsigned int i, free_idx = ADV7511_MAX_ADDRS;
2333b1b9750SHans Verkuil 
2343b1b9750SHans Verkuil 	if (!adv7511->cec_enabled_adap)
2353b1b9750SHans Verkuil 		return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;
2363b1b9750SHans Verkuil 
2373b1b9750SHans Verkuil 	if (addr == CEC_LOG_ADDR_INVALID) {
2383b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap_cec,
2393b1b9750SHans Verkuil 				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
2403b1b9750SHans Verkuil 				   0x70, 0);
2413b1b9750SHans Verkuil 		adv7511->cec_valid_addrs = 0;
2423b1b9750SHans Verkuil 		return 0;
2433b1b9750SHans Verkuil 	}
2443b1b9750SHans Verkuil 
2453b1b9750SHans Verkuil 	for (i = 0; i < ADV7511_MAX_ADDRS; i++) {
2463b1b9750SHans Verkuil 		bool is_valid = adv7511->cec_valid_addrs & (1 << i);
2473b1b9750SHans Verkuil 
2483b1b9750SHans Verkuil 		if (free_idx == ADV7511_MAX_ADDRS && !is_valid)
2493b1b9750SHans Verkuil 			free_idx = i;
2503b1b9750SHans Verkuil 		if (is_valid && adv7511->cec_addr[i] == addr)
2513b1b9750SHans Verkuil 			return 0;
2523b1b9750SHans Verkuil 	}
2533b1b9750SHans Verkuil 	if (i == ADV7511_MAX_ADDRS) {
2543b1b9750SHans Verkuil 		i = free_idx;
2553b1b9750SHans Verkuil 		if (i == ADV7511_MAX_ADDRS)
2563b1b9750SHans Verkuil 			return -ENXIO;
2573b1b9750SHans Verkuil 	}
2583b1b9750SHans Verkuil 	adv7511->cec_addr[i] = addr;
2593b1b9750SHans Verkuil 	adv7511->cec_valid_addrs |= 1 << i;
2603b1b9750SHans Verkuil 
2613b1b9750SHans Verkuil 	switch (i) {
2623b1b9750SHans Verkuil 	case 0:
2633b1b9750SHans Verkuil 		/* enable address mask 0 */
2643b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap_cec,
2653b1b9750SHans Verkuil 				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
2663b1b9750SHans Verkuil 				   0x10, 0x10);
2673b1b9750SHans Verkuil 		/* set address for mask 0 */
2683b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap_cec,
2693b1b9750SHans Verkuil 				   ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
2703b1b9750SHans Verkuil 				   0x0f, addr);
2713b1b9750SHans Verkuil 		break;
2723b1b9750SHans Verkuil 	case 1:
2733b1b9750SHans Verkuil 		/* enable address mask 1 */
2743b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap_cec,
2753b1b9750SHans Verkuil 				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
2763b1b9750SHans Verkuil 				   0x20, 0x20);
2773b1b9750SHans Verkuil 		/* set address for mask 1 */
2783b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap_cec,
2793b1b9750SHans Verkuil 				   ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,
2803b1b9750SHans Verkuil 				   0xf0, addr << 4);
2813b1b9750SHans Verkuil 		break;
2823b1b9750SHans Verkuil 	case 2:
2833b1b9750SHans Verkuil 		/* enable address mask 2 */
2843b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap_cec,
2853b1b9750SHans Verkuil 				   ADV7511_REG_CEC_LOG_ADDR_MASK + offset,
2863b1b9750SHans Verkuil 				   0x40, 0x40);
2873b1b9750SHans Verkuil 		/* set address for mask 1 */
2883b1b9750SHans Verkuil 		regmap_update_bits(adv7511->regmap_cec,
2893b1b9750SHans Verkuil 				   ADV7511_REG_CEC_LOG_ADDR_2 + offset,
2903b1b9750SHans Verkuil 				   0x0f, addr);
2913b1b9750SHans Verkuil 		break;
2923b1b9750SHans Verkuil 	}
2933b1b9750SHans Verkuil 	return 0;
2943b1b9750SHans Verkuil }
2953b1b9750SHans Verkuil 
adv7511_cec_adap_transmit(struct cec_adapter * adap,u8 attempts,u32 signal_free_time,struct cec_msg * msg)2963b1b9750SHans Verkuil static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
2973b1b9750SHans Verkuil 				     u32 signal_free_time, struct cec_msg *msg)
2983b1b9750SHans Verkuil {
2993b1b9750SHans Verkuil 	struct adv7511 *adv7511 = cec_get_drvdata(adap);
3008d6cf571SBiju Das 	unsigned int offset = adv7511->info->reg_cec_offset;
3013b1b9750SHans Verkuil 	u8 len = msg->len;
3023b1b9750SHans Verkuil 	unsigned int i;
3033b1b9750SHans Verkuil 
3043b1b9750SHans Verkuil 	/*
3053b1b9750SHans Verkuil 	 * The number of retries is the number of attempts - 1, but retry
3063b1b9750SHans Verkuil 	 * at least once. It's not clear if a value of 0 is allowed, so
3073b1b9750SHans Verkuil 	 * let's do at least one retry.
3083b1b9750SHans Verkuil 	 */
3093b1b9750SHans Verkuil 	regmap_update_bits(adv7511->regmap_cec,
3103b1b9750SHans Verkuil 			   ADV7511_REG_CEC_TX_RETRY + offset,
3113b1b9750SHans Verkuil 			   0x70, max(1, attempts - 1) << 4);
3123b1b9750SHans Verkuil 
3133b1b9750SHans Verkuil 	/* blocking, clear cec tx irq status */
3143b1b9750SHans Verkuil 	regmap_update_bits(adv7511->regmap, ADV7511_REG_INT(1), 0x38, 0x38);
3153b1b9750SHans Verkuil 
3163b1b9750SHans Verkuil 	/* write data */
3173b1b9750SHans Verkuil 	for (i = 0; i < len; i++)
3183b1b9750SHans Verkuil 		regmap_write(adv7511->regmap_cec,
3193b1b9750SHans Verkuil 			     i + ADV7511_REG_CEC_TX_FRAME_HDR + offset,
3203b1b9750SHans Verkuil 			     msg->msg[i]);
3213b1b9750SHans Verkuil 
3223b1b9750SHans Verkuil 	/* set length (data + header) */
3233b1b9750SHans Verkuil 	regmap_write(adv7511->regmap_cec,
3243b1b9750SHans Verkuil 		     ADV7511_REG_CEC_TX_FRAME_LEN + offset, len);
3253b1b9750SHans Verkuil 	/* start transmit, enable tx */
3263b1b9750SHans Verkuil 	regmap_write(adv7511->regmap_cec,
3273b1b9750SHans Verkuil 		     ADV7511_REG_CEC_TX_ENABLE + offset, 0x01);
3283b1b9750SHans Verkuil 	return 0;
3293b1b9750SHans Verkuil }
3303b1b9750SHans Verkuil 
3313b1b9750SHans Verkuil static const struct cec_adap_ops adv7511_cec_adap_ops = {
3323b1b9750SHans Verkuil 	.adap_enable = adv7511_cec_adap_enable,
3333b1b9750SHans Verkuil 	.adap_log_addr = adv7511_cec_adap_log_addr,
3343b1b9750SHans Verkuil 	.adap_transmit = adv7511_cec_adap_transmit,
3353b1b9750SHans Verkuil };
3363b1b9750SHans Verkuil 
adv7511_cec_parse_dt(struct device * dev,struct adv7511 * adv7511)3373b1b9750SHans Verkuil static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511)
3383b1b9750SHans Verkuil {
3393b1b9750SHans Verkuil 	adv7511->cec_clk = devm_clk_get(dev, "cec");
3403b1b9750SHans Verkuil 	if (IS_ERR(adv7511->cec_clk)) {
3413b1b9750SHans Verkuil 		int ret = PTR_ERR(adv7511->cec_clk);
3423b1b9750SHans Verkuil 
3433b1b9750SHans Verkuil 		adv7511->cec_clk = NULL;
3443b1b9750SHans Verkuil 		return ret;
3453b1b9750SHans Verkuil 	}
3463b1b9750SHans Verkuil 	clk_prepare_enable(adv7511->cec_clk);
3473b1b9750SHans Verkuil 	adv7511->cec_clk_freq = clk_get_rate(adv7511->cec_clk);
3483b1b9750SHans Verkuil 	return 0;
3493b1b9750SHans Verkuil }
3503b1b9750SHans Verkuil 
adv7511_cec_init(struct device * dev,struct adv7511 * adv7511)3511b6fba45SHans Verkuil int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511)
3523b1b9750SHans Verkuil {
3538d6cf571SBiju Das 	unsigned int offset = adv7511->info->reg_cec_offset;
3543b1b9750SHans Verkuil 	int ret = adv7511_cec_parse_dt(dev, adv7511);
3553b1b9750SHans Verkuil 
3563b1b9750SHans Verkuil 	if (ret)
3571b6fba45SHans Verkuil 		goto err_cec_parse_dt;
3583b1b9750SHans Verkuil 
3593b1b9750SHans Verkuil 	adv7511->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
3603b1b9750SHans Verkuil 		adv7511, dev_name(dev), CEC_CAP_DEFAULTS, ADV7511_MAX_ADDRS);
3611b6fba45SHans Verkuil 	if (IS_ERR(adv7511->cec_adap)) {
3621b6fba45SHans Verkuil 		ret = PTR_ERR(adv7511->cec_adap);
3631b6fba45SHans Verkuil 		goto err_cec_alloc;
3641b6fba45SHans Verkuil 	}
3653b1b9750SHans Verkuil 
3661d22b603SAlvin Šipraga 	regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, 0);
3673b1b9750SHans Verkuil 	/* cec soft reset */
3683b1b9750SHans Verkuil 	regmap_write(adv7511->regmap_cec,
3693b1b9750SHans Verkuil 		     ADV7511_REG_CEC_SOFT_RESET + offset, 0x01);
3703b1b9750SHans Verkuil 	regmap_write(adv7511->regmap_cec,
3713b1b9750SHans Verkuil 		     ADV7511_REG_CEC_SOFT_RESET + offset, 0x00);
3723b1b9750SHans Verkuil 
373ab0af093SAlvin Šipraga 	/* non-legacy mode - use all three RX buffers */
3743b1b9750SHans Verkuil 	regmap_write(adv7511->regmap_cec,
375ab0af093SAlvin Šipraga 		     ADV7511_REG_CEC_RX_BUFFERS + offset, 0x08);
3763b1b9750SHans Verkuil 
3773b1b9750SHans Verkuil 	regmap_write(adv7511->regmap_cec,
3783b1b9750SHans Verkuil 		     ADV7511_REG_CEC_CLK_DIV + offset,
3793b1b9750SHans Verkuil 		     ((adv7511->cec_clk_freq / 750000) - 1) << 2);
3803b1b9750SHans Verkuil 
3813b1b9750SHans Verkuil 	ret = cec_register_adapter(adv7511->cec_adap, dev);
3821b6fba45SHans Verkuil 	if (ret)
3831b6fba45SHans Verkuil 		goto err_cec_register;
3841b6fba45SHans Verkuil 	return 0;
3851b6fba45SHans Verkuil 
3861b6fba45SHans Verkuil err_cec_register:
3873b1b9750SHans Verkuil 	cec_delete_adapter(adv7511->cec_adap);
3883b1b9750SHans Verkuil 	adv7511->cec_adap = NULL;
3891b6fba45SHans Verkuil err_cec_alloc:
3901b6fba45SHans Verkuil 	dev_info(dev, "Initializing CEC failed with error %d, disabling CEC\n",
3911b6fba45SHans Verkuil 		 ret);
3921b6fba45SHans Verkuil err_cec_parse_dt:
3931d22b603SAlvin Šipraga 	regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL,
3941b6fba45SHans Verkuil 		     ADV7511_CEC_CTRL_POWER_DOWN);
3951b6fba45SHans Verkuil 	return ret == -EPROBE_DEFER ? ret : 0;
3963b1b9750SHans Verkuil }
397