397 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * adv7511_cec.c - Analog Devices ADV7511/33 cec driver4 *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 performs60 * 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 122int 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 int irq_status = IRQ_NONE;135 136 if (irq1 & irq_tx_mask) {137 adv_cec_tx_raw_status(adv7511, irq1);138 irq_status = IRQ_HANDLED;139 }140 141 if (!(irq1 & irq_rx_mask))142 return irq_status;143 144 if (regmap_read(adv7511->regmap_cec,145 ADV7511_REG_CEC_RX_STATUS + offset, &rx_status))146 return irq_status;147 148 /*149 * ADV7511_REG_CEC_RX_STATUS[5:0] contains the reception order of RX150 * buffers 0, 1, and 2 in bits [1:0], [3:2], and [5:4] respectively.151 * The values are to be interpreted as follows:152 *153 * 0 = buffer unused154 * 1 = buffer contains oldest received frame (if applicable)155 * 2 = buffer contains second oldest received frame (if applicable)156 * 3 = buffer contains third oldest received frame (if applicable)157 *158 * Fill rx_order with the sequence of RX buffer indices to159 * read from in order, where -1 indicates that there are no160 * more buffers to process.161 */162 for (i = 0; i < 3; i++) {163 unsigned int timestamp = (rx_status >> (2 * i)) & 0x3;164 165 if (timestamp)166 rx_order[timestamp - 1] = i;167 }168 169 /* Read CEC RX buffers in the appropriate order as prescribed above */170 for (i = 0; i < 3; i++) {171 int rx_buf = rx_order[i];172 173 if (rx_buf < 0)174 break;175 176 adv7511_cec_rx(adv7511, rx_buf);177 }178 179 return IRQ_HANDLED;180}181 182static int adv7511_cec_adap_enable(struct cec_adapter *adap, bool enable)183{184 struct adv7511 *adv7511 = cec_get_drvdata(adap);185 unsigned int offset = adv7511->info->reg_cec_offset;186 187 if (adv7511->i2c_cec == NULL)188 return -EIO;189 190 if (!adv7511->cec_enabled_adap && enable) {191 /* power up cec section */192 regmap_update_bits(adv7511->regmap_cec,193 ADV7511_REG_CEC_CLK_DIV + offset,194 0x03, 0x01);195 /* non-legacy mode and clear all rx buffers */196 regmap_write(adv7511->regmap_cec,197 ADV7511_REG_CEC_RX_BUFFERS + offset, 0x0f);198 regmap_write(adv7511->regmap_cec,199 ADV7511_REG_CEC_RX_BUFFERS + offset, 0x08);200 /* initially disable tx */201 regmap_update_bits(adv7511->regmap_cec,202 ADV7511_REG_CEC_TX_ENABLE + offset, 1, 0);203 /* enabled irqs: */204 /* tx: ready */205 /* tx: arbitration lost */206 /* tx: retry timeout */207 /* rx: ready 1-3 */208 regmap_update_bits(adv7511->regmap,209 ADV7511_REG_INT_ENABLE(1), 0x3f,210 ADV7511_INT1_CEC_MASK);211 } else if (adv7511->cec_enabled_adap && !enable) {212 regmap_update_bits(adv7511->regmap,213 ADV7511_REG_INT_ENABLE(1), 0x3f, 0);214 /* disable address mask 1-3 */215 regmap_update_bits(adv7511->regmap_cec,216 ADV7511_REG_CEC_LOG_ADDR_MASK + offset,217 0x70, 0x00);218 /* power down cec section */219 regmap_update_bits(adv7511->regmap_cec,220 ADV7511_REG_CEC_CLK_DIV + offset,221 0x03, 0x00);222 adv7511->cec_valid_addrs = 0;223 }224 adv7511->cec_enabled_adap = enable;225 return 0;226}227 228static int adv7511_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)229{230 struct adv7511 *adv7511 = cec_get_drvdata(adap);231 unsigned int offset = adv7511->info->reg_cec_offset;232 unsigned int i, free_idx = ADV7511_MAX_ADDRS;233 234 if (!adv7511->cec_enabled_adap)235 return addr == CEC_LOG_ADDR_INVALID ? 0 : -EIO;236 237 if (addr == CEC_LOG_ADDR_INVALID) {238 regmap_update_bits(adv7511->regmap_cec,239 ADV7511_REG_CEC_LOG_ADDR_MASK + offset,240 0x70, 0);241 adv7511->cec_valid_addrs = 0;242 return 0;243 }244 245 for (i = 0; i < ADV7511_MAX_ADDRS; i++) {246 bool is_valid = adv7511->cec_valid_addrs & (1 << i);247 248 if (free_idx == ADV7511_MAX_ADDRS && !is_valid)249 free_idx = i;250 if (is_valid && adv7511->cec_addr[i] == addr)251 return 0;252 }253 if (i == ADV7511_MAX_ADDRS) {254 i = free_idx;255 if (i == ADV7511_MAX_ADDRS)256 return -ENXIO;257 }258 adv7511->cec_addr[i] = addr;259 adv7511->cec_valid_addrs |= 1 << i;260 261 switch (i) {262 case 0:263 /* enable address mask 0 */264 regmap_update_bits(adv7511->regmap_cec,265 ADV7511_REG_CEC_LOG_ADDR_MASK + offset,266 0x10, 0x10);267 /* set address for mask 0 */268 regmap_update_bits(adv7511->regmap_cec,269 ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,270 0x0f, addr);271 break;272 case 1:273 /* enable address mask 1 */274 regmap_update_bits(adv7511->regmap_cec,275 ADV7511_REG_CEC_LOG_ADDR_MASK + offset,276 0x20, 0x20);277 /* set address for mask 1 */278 regmap_update_bits(adv7511->regmap_cec,279 ADV7511_REG_CEC_LOG_ADDR_0_1 + offset,280 0xf0, addr << 4);281 break;282 case 2:283 /* enable address mask 2 */284 regmap_update_bits(adv7511->regmap_cec,285 ADV7511_REG_CEC_LOG_ADDR_MASK + offset,286 0x40, 0x40);287 /* set address for mask 1 */288 regmap_update_bits(adv7511->regmap_cec,289 ADV7511_REG_CEC_LOG_ADDR_2 + offset,290 0x0f, addr);291 break;292 }293 return 0;294}295 296static int adv7511_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,297 u32 signal_free_time, struct cec_msg *msg)298{299 struct adv7511 *adv7511 = cec_get_drvdata(adap);300 unsigned int offset = adv7511->info->reg_cec_offset;301 u8 len = msg->len;302 unsigned int i;303 304 /*305 * The number of retries is the number of attempts - 1, but retry306 * at least once. It's not clear if a value of 0 is allowed, so307 * let's do at least one retry.308 */309 regmap_update_bits(adv7511->regmap_cec,310 ADV7511_REG_CEC_TX_RETRY + offset,311 0x70, max(1, attempts - 1) << 4);312 313 /* blocking, clear cec tx irq status */314 regmap_update_bits(adv7511->regmap, ADV7511_REG_INT(1), 0x38, 0x38);315 316 /* write data */317 for (i = 0; i < len; i++)318 regmap_write(adv7511->regmap_cec,319 i + ADV7511_REG_CEC_TX_FRAME_HDR + offset,320 msg->msg[i]);321 322 /* set length (data + header) */323 regmap_write(adv7511->regmap_cec,324 ADV7511_REG_CEC_TX_FRAME_LEN + offset, len);325 /* start transmit, enable tx */326 regmap_write(adv7511->regmap_cec,327 ADV7511_REG_CEC_TX_ENABLE + offset, 0x01);328 return 0;329}330 331static const struct cec_adap_ops adv7511_cec_adap_ops = {332 .adap_enable = adv7511_cec_adap_enable,333 .adap_log_addr = adv7511_cec_adap_log_addr,334 .adap_transmit = adv7511_cec_adap_transmit,335};336 337static int adv7511_cec_parse_dt(struct device *dev, struct adv7511 *adv7511)338{339 adv7511->cec_clk = devm_clk_get(dev, "cec");340 if (IS_ERR(adv7511->cec_clk)) {341 int ret = PTR_ERR(adv7511->cec_clk);342 343 adv7511->cec_clk = NULL;344 return ret;345 }346 clk_prepare_enable(adv7511->cec_clk);347 adv7511->cec_clk_freq = clk_get_rate(adv7511->cec_clk);348 return 0;349}350 351int adv7511_cec_init(struct device *dev, struct adv7511 *adv7511)352{353 unsigned int offset = adv7511->info->reg_cec_offset;354 int ret = adv7511_cec_parse_dt(dev, adv7511);355 356 if (ret)357 goto err_cec_parse_dt;358 359 adv7511->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,360 adv7511, dev_name(dev), CEC_CAP_DEFAULTS, ADV7511_MAX_ADDRS);361 if (IS_ERR(adv7511->cec_adap)) {362 ret = PTR_ERR(adv7511->cec_adap);363 goto err_cec_alloc;364 }365 366 regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL, 0);367 /* cec soft reset */368 regmap_write(adv7511->regmap_cec,369 ADV7511_REG_CEC_SOFT_RESET + offset, 0x01);370 regmap_write(adv7511->regmap_cec,371 ADV7511_REG_CEC_SOFT_RESET + offset, 0x00);372 373 /* non-legacy mode - use all three RX buffers */374 regmap_write(adv7511->regmap_cec,375 ADV7511_REG_CEC_RX_BUFFERS + offset, 0x08);376 377 regmap_write(adv7511->regmap_cec,378 ADV7511_REG_CEC_CLK_DIV + offset,379 ((adv7511->cec_clk_freq / 750000) - 1) << 2);380 381 ret = cec_register_adapter(adv7511->cec_adap, dev);382 if (ret)383 goto err_cec_register;384 return 0;385 386err_cec_register:387 cec_delete_adapter(adv7511->cec_adap);388 adv7511->cec_adap = NULL;389err_cec_alloc:390 dev_info(dev, "Initializing CEC failed with error %d, disabling CEC\n",391 ret);392err_cec_parse_dt:393 regmap_write(adv7511->regmap, ADV7511_REG_CEC_CTRL,394 ADV7511_CEC_CTRL_POWER_DOWN);395 return ret == -EPROBE_DEFER ? ret : 0;396}397