603 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Driver for the Chrontel CH7322 CEC Controller4 *5 * Copyright 2020 Google LLC.6 */7 8/*9 * Notes10 *11 * - This device powers on in Auto Mode which has limited functionality. This12 * driver disables Auto Mode when it attaches.13 *14 */15 16#include <linux/cec.h>17#include <linux/dmi.h>18#include <linux/i2c.h>19#include <linux/interrupt.h>20#include <linux/module.h>21#include <linux/mutex.h>22#include <linux/pci.h>23#include <linux/regmap.h>24#include <media/cec.h>25#include <media/cec-notifier.h>26 27#define CH7322_WRITE 0x0028#define CH7322_WRITE_MSENT 0x8029#define CH7322_WRITE_BOK 0x4030#define CH7322_WRITE_NMASK 0x0f31 32/* Write buffer is 0x01-0x10 */33#define CH7322_WRBUF 0x0134#define CH7322_WRBUF_LEN 0x1035 36#define CH7322_READ 0x4037#define CH7322_READ_NRDT 0x8038#define CH7322_READ_MSENT 0x2039#define CH7322_READ_NMASK 0x0f40 41/* Read buffer is 0x41-0x50 */42#define CH7322_RDBUF 0x4143#define CH7322_RDBUF_LEN 0x1044 45#define CH7322_MODE 0x1146#define CH7322_MODE_AUTO 0x7847#define CH7322_MODE_SW 0xb548 49#define CH7322_RESET 0x1250#define CH7322_RESET_RST 0x0051 52#define CH7322_POWER 0x1353#define CH7322_POWER_FPD 0x0454 55#define CH7322_CFG0 0x1756#define CH7322_CFG0_EOBEN 0x4057#define CH7322_CFG0_PEOB 0x2058#define CH7322_CFG0_CLRSPP 0x1059#define CH7322_CFG0_FLOW 0x0860 61#define CH7322_CFG1 0x1a62#define CH7322_CFG1_STDBYO 0x0463#define CH7322_CFG1_HPBP 0x0264#define CH7322_CFG1_PIO 0x0165 66#define CH7322_INTCTL 0x1b67#define CH7322_INTCTL_INTPB 0x8068#define CH7322_INTCTL_STDBY 0x4069#define CH7322_INTCTL_HPDFALL 0x2070#define CH7322_INTCTL_HPDRISE 0x1071#define CH7322_INTCTL_RXMSG 0x0872#define CH7322_INTCTL_TXMSG 0x0473#define CH7322_INTCTL_NEWPHA 0x0274#define CH7322_INTCTL_ERROR 0x0175 76#define CH7322_DVCLKFNH 0x1d77#define CH7322_DVCLKFNL 0x1e78 79#define CH7322_CTL 0x3180#define CH7322_CTL_FSTDBY 0x8081#define CH7322_CTL_PLSEN 0x4082#define CH7322_CTL_PLSPB 0x2083#define CH7322_CTL_SPADL 0x1084#define CH7322_CTL_HINIT 0x0885#define CH7322_CTL_WPHYA 0x0486#define CH7322_CTL_H1T 0x0287#define CH7322_CTL_S1T 0x0188 89#define CH7322_PAWH 0x3290#define CH7322_PAWL 0x3391 92#define CH7322_ADDLW 0x3493#define CH7322_ADDLW_MASK 0xf094 95#define CH7322_ADDLR 0x3d96#define CH7322_ADDLR_HPD 0x8097#define CH7322_ADDLR_MASK 0x0f98 99#define CH7322_INTDATA 0x3e100#define CH7322_INTDATA_MODE 0x80101#define CH7322_INTDATA_STDBY 0x40102#define CH7322_INTDATA_HPDFALL 0x20103#define CH7322_INTDATA_HPDRISE 0x10104#define CH7322_INTDATA_RXMSG 0x08105#define CH7322_INTDATA_TXMSG 0x04106#define CH7322_INTDATA_NEWPHA 0x02107#define CH7322_INTDATA_ERROR 0x01108 109#define CH7322_EVENT 0x3f110#define CH7322_EVENT_TXERR 0x80111#define CH7322_EVENT_HRST 0x40112#define CH7322_EVENT_HFST 0x20113#define CH7322_EVENT_PHACHG 0x10114#define CH7322_EVENT_ACTST 0x08115#define CH7322_EVENT_PHARDY 0x04116#define CH7322_EVENT_BSOK 0x02117#define CH7322_EVENT_ERRADCF 0x01118 119#define CH7322_DID 0x51120#define CH7322_DID_CH7322 0x5b121#define CH7322_DID_CH7323 0x5f122 123#define CH7322_REVISIONID 0x52124 125#define CH7322_PARH 0x53126#define CH7322_PARL 0x54127 128#define CH7322_IOCFG2 0x75129#define CH7322_IOCFG_CIO 0x80130#define CH7322_IOCFG_IOCFGMASK 0x78131#define CH7322_IOCFG_AUDIO 0x04132#define CH7322_IOCFG_SPAMST 0x02133#define CH7322_IOCFG_SPAMSP 0x01134 135#define CH7322_CTL3 0x7b136#define CH7322_CTL3_SWENA 0x80137#define CH7322_CTL3_FC_INIT 0x40138#define CH7322_CTL3_SML_FL 0x20139#define CH7322_CTL3_SM_RDST 0x10140#define CH7322_CTL3_SPP_CIAH 0x08141#define CH7322_CTL3_SPP_CIAL 0x04142#define CH7322_CTL3_SPP_ACTH 0x02143#define CH7322_CTL3_SPP_ACTL 0x01144 145/* BOK status means NACK */146#define CH7322_TX_FLAG_NACK BIT(0)147/* Device will retry automatically */148#define CH7322_TX_FLAG_RETRY BIT(1)149 150struct ch7322 {151 struct i2c_client *i2c;152 struct regmap *regmap;153 struct cec_adapter *cec;154 struct mutex mutex; /* device access mutex */155 u8 tx_flags;156};157 158static const struct regmap_config ch7322_regmap = {159 .reg_bits = 8,160 .val_bits = 8,161 .max_register = 0x7f,162 .disable_locking = true,163};164 165static int ch7322_send_message(struct ch7322 *ch7322, const struct cec_msg *msg)166{167 unsigned int val;168 unsigned int len = msg->len;169 int ret;170 int i;171 172 WARN_ON(!mutex_is_locked(&ch7322->mutex));173 174 if (len > CH7322_WRBUF_LEN || len < 1)175 return -EINVAL;176 177 ret = regmap_read(ch7322->regmap, CH7322_WRITE, &val);178 if (ret)179 return ret;180 181 /* Buffer not ready */182 if (!(val & CH7322_WRITE_MSENT))183 return -EBUSY;184 185 if (cec_msg_opcode(msg) == -1 &&186 cec_msg_initiator(msg) == cec_msg_destination(msg)) {187 ch7322->tx_flags = CH7322_TX_FLAG_NACK | CH7322_TX_FLAG_RETRY;188 } else if (cec_msg_is_broadcast(msg)) {189 ch7322->tx_flags = CH7322_TX_FLAG_NACK;190 } else {191 ch7322->tx_flags = CH7322_TX_FLAG_RETRY;192 }193 194 ret = regmap_write(ch7322->regmap, CH7322_WRITE, len - 1);195 if (ret)196 return ret;197 198 for (i = 0; i < len; i++) {199 ret = regmap_write(ch7322->regmap,200 CH7322_WRBUF + i, msg->msg[i]);201 if (ret)202 return ret;203 }204 205 return 0;206}207 208static int ch7322_receive_message(struct ch7322 *ch7322, struct cec_msg *msg)209{210 unsigned int val;211 int ret = 0;212 int i;213 214 WARN_ON(!mutex_is_locked(&ch7322->mutex));215 216 ret = regmap_read(ch7322->regmap, CH7322_READ, &val);217 if (ret)218 return ret;219 220 /* Message not ready */221 if (!(val & CH7322_READ_NRDT))222 return -EIO;223 224 msg->len = (val & CH7322_READ_NMASK) + 1;225 226 /* Read entire RDBUF to clear state */227 for (i = 0; i < CH7322_RDBUF_LEN; i++) {228 ret = regmap_read(ch7322->regmap, CH7322_RDBUF + i, &val);229 if (ret)230 return ret;231 msg->msg[i] = (u8)val;232 }233 234 return 0;235}236 237static void ch7322_tx_done(struct ch7322 *ch7322)238{239 int ret;240 unsigned int val;241 u8 status, flags;242 243 mutex_lock(&ch7322->mutex);244 ret = regmap_read(ch7322->regmap, CH7322_WRITE, &val);245 flags = ch7322->tx_flags;246 mutex_unlock(&ch7322->mutex);247 248 /*249 * The device returns a one-bit OK status which usually means ACK but250 * actually means NACK when sending a logical address query or a251 * broadcast.252 */253 if (ret)254 status = CEC_TX_STATUS_ERROR;255 else if ((val & CH7322_WRITE_BOK) && (flags & CH7322_TX_FLAG_NACK))256 status = CEC_TX_STATUS_NACK;257 else if (val & CH7322_WRITE_BOK)258 status = CEC_TX_STATUS_OK;259 else if (flags & CH7322_TX_FLAG_NACK)260 status = CEC_TX_STATUS_OK;261 else262 status = CEC_TX_STATUS_NACK;263 264 if (status == CEC_TX_STATUS_NACK && (flags & CH7322_TX_FLAG_RETRY))265 status |= CEC_TX_STATUS_MAX_RETRIES;266 267 cec_transmit_attempt_done(ch7322->cec, status);268}269 270static void ch7322_rx_done(struct ch7322 *ch7322)271{272 struct cec_msg msg;273 int ret;274 275 mutex_lock(&ch7322->mutex);276 ret = ch7322_receive_message(ch7322, &msg);277 mutex_unlock(&ch7322->mutex);278 279 if (ret)280 dev_err(&ch7322->i2c->dev, "cec receive error: %d\n", ret);281 else282 cec_received_msg(ch7322->cec, &msg);283}284 285/*286 * This device can either monitor the DDC lines to obtain the physical address287 * or it can allow the host to program it. This driver lets the device obtain288 * it.289 */290static void ch7322_phys_addr(struct ch7322 *ch7322)291{292 unsigned int pah, pal;293 int ret = 0;294 295 mutex_lock(&ch7322->mutex);296 ret |= regmap_read(ch7322->regmap, CH7322_PARH, &pah);297 ret |= regmap_read(ch7322->regmap, CH7322_PARL, &pal);298 mutex_unlock(&ch7322->mutex);299 300 if (ret)301 dev_err(&ch7322->i2c->dev, "phys addr error\n");302 else303 cec_s_phys_addr(ch7322->cec, pal | (pah << 8), false);304}305 306static irqreturn_t ch7322_irq(int irq, void *dev)307{308 struct ch7322 *ch7322 = dev;309 unsigned int data = 0;310 311 mutex_lock(&ch7322->mutex);312 regmap_read(ch7322->regmap, CH7322_INTDATA, &data);313 regmap_write(ch7322->regmap, CH7322_INTDATA, data);314 mutex_unlock(&ch7322->mutex);315 316 if (data & CH7322_INTDATA_HPDFALL)317 cec_phys_addr_invalidate(ch7322->cec);318 319 if (data & CH7322_INTDATA_TXMSG)320 ch7322_tx_done(ch7322);321 322 if (data & CH7322_INTDATA_RXMSG)323 ch7322_rx_done(ch7322);324 325 if (data & CH7322_INTDATA_NEWPHA)326 ch7322_phys_addr(ch7322);327 328 if (data & CH7322_INTDATA_ERROR)329 dev_dbg(&ch7322->i2c->dev, "unknown error\n");330 331 return IRQ_HANDLED;332}333 334/* This device is always enabled */335static int ch7322_cec_adap_enable(struct cec_adapter *adap, bool enable)336{337 return 0;338}339 340static int ch7322_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)341{342 struct ch7322 *ch7322 = cec_get_drvdata(adap);343 int ret;344 345 mutex_lock(&ch7322->mutex);346 ret = regmap_update_bits(ch7322->regmap, CH7322_ADDLW,347 CH7322_ADDLW_MASK, log_addr << 4);348 mutex_unlock(&ch7322->mutex);349 350 return ret;351}352 353static int ch7322_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,354 u32 signal_free_time, struct cec_msg *msg)355{356 struct ch7322 *ch7322 = cec_get_drvdata(adap);357 int ret;358 359 mutex_lock(&ch7322->mutex);360 ret = ch7322_send_message(ch7322, msg);361 mutex_unlock(&ch7322->mutex);362 363 return ret;364}365 366static const struct cec_adap_ops ch7322_cec_adap_ops = {367 .adap_enable = ch7322_cec_adap_enable,368 .adap_log_addr = ch7322_cec_adap_log_addr,369 .adap_transmit = ch7322_cec_adap_transmit,370};371 372#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI)373 374struct ch7322_conn_match {375 const char *dev_name;376 const char *pci_name;377 const char *port_name;378};379 380static struct ch7322_conn_match google_endeavour[] = {381 { "i2c-PRP0001:00", "0000:00:02.0", "Port B" },382 { "i2c-PRP0001:01", "0000:00:02.0", "Port C" },383 { },384};385 386static const struct dmi_system_id ch7322_dmi_table[] = {387 {388 .matches = {389 DMI_MATCH(DMI_BOARD_VENDOR, "Google"),390 DMI_MATCH(DMI_BOARD_NAME, "Endeavour"),391 },392 .driver_data = google_endeavour,393 },394 { },395};396 397/* Make a best-effort attempt to locate a matching HDMI port */398static int ch7322_get_port(struct i2c_client *client,399 struct device **dev,400 const char **port)401{402 const struct dmi_system_id *system;403 const struct ch7322_conn_match *conn;404 405 *dev = NULL;406 *port = NULL;407 408 system = dmi_first_match(ch7322_dmi_table);409 if (!system)410 return 0;411 412 for (conn = system->driver_data; conn->dev_name; conn++) {413 if (!strcmp(dev_name(&client->dev), conn->dev_name)) {414 struct device *d;415 416 d = bus_find_device_by_name(&pci_bus_type, NULL,417 conn->pci_name);418 if (!d)419 return -EPROBE_DEFER;420 421 put_device(d);422 423 *dev = d;424 *port = conn->port_name;425 426 return 0;427 }428 }429 430 return 0;431}432 433#else434 435static int ch7322_get_port(struct i2c_client *client,436 struct device **dev,437 const char **port)438{439 *dev = NULL;440 *port = NULL;441 442 return 0;443}444 445#endif446 447static int ch7322_probe(struct i2c_client *client)448{449 struct device *hdmi_dev;450 const char *port_name;451 struct ch7322 *ch7322;452 struct cec_notifier *notifier = NULL;453 u32 caps = CEC_CAP_DEFAULTS;454 int ret;455 unsigned int val;456 457 ret = ch7322_get_port(client, &hdmi_dev, &port_name);458 if (ret)459 return ret;460 461 if (hdmi_dev)462 caps |= CEC_CAP_CONNECTOR_INFO;463 464 ch7322 = devm_kzalloc(&client->dev, sizeof(*ch7322), GFP_KERNEL);465 if (!ch7322)466 return -ENOMEM;467 468 ch7322->regmap = devm_regmap_init_i2c(client, &ch7322_regmap);469 if (IS_ERR(ch7322->regmap))470 return PTR_ERR(ch7322->regmap);471 472 ret = regmap_read(ch7322->regmap, CH7322_DID, &val);473 if (ret)474 return ret;475 476 if (val != CH7322_DID_CH7322)477 return -EOPNOTSUPP;478 479 mutex_init(&ch7322->mutex);480 ch7322->i2c = client;481 ch7322->tx_flags = 0;482 483 i2c_set_clientdata(client, ch7322);484 485 /* Disable auto mode */486 ret = regmap_write(ch7322->regmap, CH7322_MODE, CH7322_MODE_SW);487 if (ret)488 goto err_mutex;489 490 /* Enable logical address register */491 ret = regmap_update_bits(ch7322->regmap, CH7322_CTL,492 CH7322_CTL_SPADL, CH7322_CTL_SPADL);493 if (ret)494 goto err_mutex;495 496 ch7322->cec = cec_allocate_adapter(&ch7322_cec_adap_ops, ch7322,497 dev_name(&client->dev),498 caps, 1);499 500 if (IS_ERR(ch7322->cec)) {501 ret = PTR_ERR(ch7322->cec);502 goto err_mutex;503 }504 505 ch7322->cec->adap_controls_phys_addr = true;506 507 if (hdmi_dev) {508 notifier = cec_notifier_cec_adap_register(hdmi_dev,509 port_name,510 ch7322->cec);511 if (!notifier) {512 ret = -ENOMEM;513 goto err_cec;514 }515 }516 517 /* Configure, mask, and clear interrupt */518 ret = regmap_write(ch7322->regmap, CH7322_CFG1, 0);519 if (ret)520 goto err_notifier;521 ret = regmap_write(ch7322->regmap, CH7322_INTCTL, CH7322_INTCTL_INTPB);522 if (ret)523 goto err_notifier;524 ret = regmap_write(ch7322->regmap, CH7322_INTDATA, 0xff);525 if (ret)526 goto err_notifier;527 528 /* If HPD is up read physical address */529 ret = regmap_read(ch7322->regmap, CH7322_ADDLR, &val);530 if (ret)531 goto err_notifier;532 if (val & CH7322_ADDLR_HPD)533 ch7322_phys_addr(ch7322);534 535 ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,536 ch7322_irq,537 IRQF_ONESHOT | IRQF_TRIGGER_RISING,538 client->name, ch7322);539 if (ret)540 goto err_notifier;541 542 /* Unmask interrupt */543 mutex_lock(&ch7322->mutex);544 ret = regmap_write(ch7322->regmap, CH7322_INTCTL, 0xff);545 mutex_unlock(&ch7322->mutex);546 547 if (ret)548 goto err_notifier;549 550 ret = cec_register_adapter(ch7322->cec, &client->dev);551 if (ret)552 goto err_notifier;553 554 dev_info(&client->dev, "device registered\n");555 556 return 0;557 558err_notifier:559 if (notifier)560 cec_notifier_cec_adap_unregister(notifier, ch7322->cec);561err_cec:562 cec_delete_adapter(ch7322->cec);563err_mutex:564 mutex_destroy(&ch7322->mutex);565 return ret;566}567 568static void ch7322_remove(struct i2c_client *client)569{570 struct ch7322 *ch7322 = i2c_get_clientdata(client);571 572 /* Mask interrupt */573 mutex_lock(&ch7322->mutex);574 regmap_write(ch7322->regmap, CH7322_INTCTL, CH7322_INTCTL_INTPB);575 mutex_unlock(&ch7322->mutex);576 577 cec_unregister_adapter(ch7322->cec);578 mutex_destroy(&ch7322->mutex);579 580 dev_info(&client->dev, "device unregistered\n");581}582 583static const struct of_device_id ch7322_of_match[] = {584 { .compatible = "chrontel,ch7322", },585 {},586};587MODULE_DEVICE_TABLE(of, ch7322_of_match);588 589static struct i2c_driver ch7322_i2c_driver = {590 .driver = {591 .name = "ch7322",592 .of_match_table = ch7322_of_match,593 },594 .probe = ch7322_probe,595 .remove = ch7322_remove,596};597 598module_i2c_driver(ch7322_i2c_driver);599 600MODULE_DESCRIPTION("Chrontel CH7322 CEC Controller Driver");601MODULE_AUTHOR("Jeff Chase <jnchase@google.com>");602MODULE_LICENSE("GPL");603