brintos

brintos / linux-shallow public Read only

0
0
Text · 13.5 KiB · b875533 Raw
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