Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags   |
// SPDX-License-Identifier: GPL-2.0-only
/*
* TDA9950 Consumer Electronics Control driver
*
* The NXP TDA9950 implements the HDMI Consumer Electronics Control
* interface. The host interface is similar to a mailbox: the data
* registers starting at REG_CDR0 are written to send a command to the
* internal CPU, and replies are read from these registers.
*
* As the data registers represent a mailbox, they must be accessed
* as a single I2C transaction. See the TDA9950 data sheet for details.
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/platform_data/tda9950.h>
#include <linux/slab.h>
#include <drm/drm_edid.h>
#include <media/cec.h>
#include <media/cec-notifier.h>
enum {
<------>REG_CSR = 0x00,
<------>CSR_BUSY = BIT(7),
<------>CSR_INT = BIT(6),
<------>CSR_ERR = BIT(5),
<------>REG_CER = 0x01,
<------>REG_CVR = 0x02,
<------>REG_CCR = 0x03,
<------>CCR_RESET = BIT(7),
<------>CCR_ON = BIT(6),
<------>REG_ACKH = 0x04,
<------>REG_ACKL = 0x05,
<------>REG_CCONR = 0x06,
<------>CCONR_ENABLE_ERROR = BIT(4),
<------>CCONR_RETRY_MASK = 7,
<------>REG_CDR0 = 0x07,
<------>CDR1_REQ = 0x00,
<------>CDR1_CNF = 0x01,
<------>CDR1_IND = 0x81,
<------>CDR1_ERR = 0x82,
<------>CDR1_IER = 0x83,
<------>CDR2_CNF_SUCCESS = 0x00,
<------>CDR2_CNF_OFF_STATE = 0x80,
<------>CDR2_CNF_BAD_REQ = 0x81,
<------>CDR2_CNF_CEC_ACCESS = 0x82,
<------>CDR2_CNF_ARB_ERROR = 0x83,
<------>CDR2_CNF_BAD_TIMING = 0x84,
<------>CDR2_CNF_NACK_ADDR = 0x85,
<------>CDR2_CNF_NACK_DATA = 0x86,
};
struct tda9950_priv {
<------>struct i2c_client *client;
<------>struct device *hdmi;
<------>struct cec_adapter *adap;
<------>struct tda9950_glue *glue;
<------>u16 addresses;
<------>struct cec_msg rx_msg;
<------>struct cec_notifier *notify;
<------>bool open;
};
static int tda9950_write_range(struct i2c_client *client, u8 addr, u8 *p, int cnt)
{
<------>struct i2c_msg msg;
<------>u8 buf[CEC_MAX_MSG_SIZE + 3];
<------>int ret;
<------>if (WARN_ON(cnt > sizeof(buf) - 1))
<------><------>return -EINVAL;
<------>buf[0] = addr;
<------>memcpy(buf + 1, p, cnt);
<------>msg.addr = client->addr;
<------>msg.flags = 0;
<------>msg.len = cnt + 1;
<------>msg.buf = buf;
<------>dev_dbg(&client->dev, "wr 0x%02x: %*ph\n", addr, cnt, p);
<------>ret = i2c_transfer(client->adapter, &msg, 1);
<------>if (ret < 0)
<------><------>dev_err(&client->dev, "Error %d writing to cec:0x%x\n", ret, addr);
<------>return ret < 0 ? ret : 0;
}
static void tda9950_write(struct i2c_client *client, u8 addr, u8 val)
{
<------>tda9950_write_range(client, addr, &val, 1);
}
static int tda9950_read_range(struct i2c_client *client, u8 addr, u8 *p, int cnt)
{
<------>struct i2c_msg msg[2];
<------>int ret;
<------>msg[0].addr = client->addr;
<------>msg[0].flags = 0;
<------>msg[0].len = 1;
<------>msg[0].buf = &addr;
<------>msg[1].addr = client->addr;
<------>msg[1].flags = I2C_M_RD;
<------>msg[1].len = cnt;
<------>msg[1].buf = p;
<------>ret = i2c_transfer(client->adapter, msg, 2);
<------>if (ret < 0)
<------><------>dev_err(&client->dev, "Error %d reading from cec:0x%x\n", ret, addr);
<------>dev_dbg(&client->dev, "rd 0x%02x: %*ph\n", addr, cnt, p);
<------>return ret;
}
static u8 tda9950_read(struct i2c_client *client, u8 addr)
{
<------>int ret;
<------>u8 val;
<------>ret = tda9950_read_range(client, addr, &val, 1);
<------>if (ret < 0)
<------><------>val = 0;
<------>return val;
}
static irqreturn_t tda9950_irq(int irq, void *data)
{
<------>struct tda9950_priv *priv = data;
<------>unsigned int tx_status;
<------>u8 csr, cconr, buf[19];
<------>u8 arb_lost_cnt, nack_cnt, err_cnt;
<------>if (!priv->open)
<------><------>return IRQ_NONE;
<------>csr = tda9950_read(priv->client, REG_CSR);
<------>if (!(csr & CSR_INT))
<------><------>return IRQ_NONE;
<------>cconr = tda9950_read(priv->client, REG_CCONR) & CCONR_RETRY_MASK;
<------>tda9950_read_range(priv->client, REG_CDR0, buf, sizeof(buf));
<------>/*
<------> * This should never happen: the data sheet says that there will
<------> * always be a valid message if the interrupt line is asserted.
<------> */
<------>if (buf[0] == 0) {
<------><------>dev_warn(&priv->client->dev, "interrupt pending, but no message?\n");
<------><------>return IRQ_NONE;
<------>}
<------>switch (buf[1]) {
<------>case CDR1_CNF: /* transmit result */
<------><------>arb_lost_cnt = nack_cnt = err_cnt = 0;
<------><------>switch (buf[2]) {
<------><------>case CDR2_CNF_SUCCESS:
<------><------><------>tx_status = CEC_TX_STATUS_OK;
<------><------><------>break;
<------><------>case CDR2_CNF_ARB_ERROR:
<------><------><------>tx_status = CEC_TX_STATUS_ARB_LOST;
<------><------><------>arb_lost_cnt = cconr;
<------><------><------>break;
<------><------>case CDR2_CNF_NACK_ADDR:
<------><------><------>tx_status = CEC_TX_STATUS_NACK;
<------><------><------>nack_cnt = cconr;
<------><------><------>break;
<------><------>default: /* some other error, refer to TDA9950 docs */
<------><------><------>dev_err(&priv->client->dev, "CNF reply error 0x%02x\n",
<------><------><------><------>buf[2]);
<------><------><------>tx_status = CEC_TX_STATUS_ERROR;
<------><------><------>err_cnt = cconr;
<------><------><------>break;
<------><------>}
<------><------>/* TDA9950 executes all retries for us */
<------><------>if (tx_status != CEC_TX_STATUS_OK)
<------><------><------>tx_status |= CEC_TX_STATUS_MAX_RETRIES;
<------><------>cec_transmit_done(priv->adap, tx_status, arb_lost_cnt,
<------><------><------><------> nack_cnt, 0, err_cnt);
<------><------>break;
<------>case CDR1_IND:
<------><------>priv->rx_msg.len = buf[0] - 2;
<------><------>if (priv->rx_msg.len > CEC_MAX_MSG_SIZE)
<------><------><------>priv->rx_msg.len = CEC_MAX_MSG_SIZE;
<------><------>memcpy(priv->rx_msg.msg, buf + 2, priv->rx_msg.len);
<------><------>cec_received_msg(priv->adap, &priv->rx_msg);
<------><------>break;
<------>default: /* unknown */
<------><------>dev_err(&priv->client->dev, "unknown service id 0x%02x\n",
<------><------><------>buf[1]);
<------><------>break;
<------>}
<------>return IRQ_HANDLED;
}
static int tda9950_cec_transmit(struct cec_adapter *adap, u8 attempts,
<------><------><------><------>u32 signal_free_time, struct cec_msg *msg)
{
<------>struct tda9950_priv *priv = adap->priv;
<------>u8 buf[CEC_MAX_MSG_SIZE + 2];
<------>buf[0] = 2 + msg->len;
<------>buf[1] = CDR1_REQ;
<------>memcpy(buf + 2, msg->msg, msg->len);
<------>if (attempts > 5)
<------><------>attempts = 5;
<------>tda9950_write(priv->client, REG_CCONR, attempts);
<------>return tda9950_write_range(priv->client, REG_CDR0, buf, 2 + msg->len);
}
static int tda9950_cec_adap_log_addr(struct cec_adapter *adap, u8 addr)
{
<------>struct tda9950_priv *priv = adap->priv;
<------>u16 addresses;
<------>u8 buf[2];
<------>if (addr == CEC_LOG_ADDR_INVALID)
<------><------>addresses = priv->addresses = 0;
<------>else
<------><------>addresses = priv->addresses |= BIT(addr);
<------>/* TDA9950 doesn't want address 15 set */
<------>addresses &= 0x7fff;
<------>buf[0] = addresses >> 8;
<------>buf[1] = addresses;
<------>return tda9950_write_range(priv->client, REG_ACKH, buf, 2);
}
/*
* When operating as part of the TDA998x, we need additional handling
* to initialise and shut down the TDA9950 part of the device. These
* two hooks are provided to allow the TDA998x code to perform those
* activities.
*/
static int tda9950_glue_open(struct tda9950_priv *priv)
{
<------>int ret = 0;
<------>if (priv->glue && priv->glue->open)
<------><------>ret = priv->glue->open(priv->glue->data);
<------>priv->open = true;
<------>return ret;
}
static void tda9950_glue_release(struct tda9950_priv *priv)
{
<------>priv->open = false;
<------>if (priv->glue && priv->glue->release)
<------><------>priv->glue->release(priv->glue->data);
}
static int tda9950_open(struct tda9950_priv *priv)
{
<------>struct i2c_client *client = priv->client;
<------>int ret;
<------>ret = tda9950_glue_open(priv);
<------>if (ret)
<------><------>return ret;
<------>/* Reset the TDA9950, and wait 250ms for it to recover */
<------>tda9950_write(client, REG_CCR, CCR_RESET);
<------>msleep(250);
<------>tda9950_cec_adap_log_addr(priv->adap, CEC_LOG_ADDR_INVALID);
<------>/* Start the command processor */
<------>tda9950_write(client, REG_CCR, CCR_ON);
<------>return 0;
}
static void tda9950_release(struct tda9950_priv *priv)
{
<------>struct i2c_client *client = priv->client;
<------>int timeout = 50;
<------>u8 csr;
<------>/* Stop the command processor */
<------>tda9950_write(client, REG_CCR, 0);
<------>/* Wait up to .5s for it to signal non-busy */
<------>do {
<------><------>csr = tda9950_read(client, REG_CSR);
<------><------>if (!(csr & CSR_BUSY) || !--timeout)
<------><------><------>break;
<------><------>msleep(10);
<------>} while (1);
<------>/* Warn the user that their IRQ may die if it's shared. */
<------>if (csr & CSR_BUSY)
<------><------>dev_warn(&client->dev, "command processor failed to stop, irq%d may die (csr=0x%02x)\n",
<------><------><------> client->irq, csr);
<------>tda9950_glue_release(priv);
}
static int tda9950_cec_adap_enable(struct cec_adapter *adap, bool enable)
{
<------>struct tda9950_priv *priv = adap->priv;
<------>if (!enable) {
<------><------>tda9950_release(priv);
<------><------>return 0;
<------>} else {
<------><------>return tda9950_open(priv);
<------>}
}
static const struct cec_adap_ops tda9950_cec_ops = {
<------>.adap_enable = tda9950_cec_adap_enable,
<------>.adap_log_addr = tda9950_cec_adap_log_addr,
<------>.adap_transmit = tda9950_cec_transmit,
};
/*
* When operating as part of the TDA998x, we need to claim additional
* resources. These two hooks permit the management of those resources.
*/
static void tda9950_devm_glue_exit(void *data)
{
<------>struct tda9950_glue *glue = data;
<------>if (glue && glue->exit)
<------><------>glue->exit(glue->data);
}
static int tda9950_devm_glue_init(struct device *dev, struct tda9950_glue *glue)
{
<------>int ret;
<------>if (glue && glue->init) {
<------><------>ret = glue->init(glue->data);
<------><------>if (ret)
<------><------><------>return ret;
<------>}
<------>ret = devm_add_action(dev, tda9950_devm_glue_exit, glue);
<------>if (ret)
<------><------>tda9950_devm_glue_exit(glue);
<------>return ret;
}
static void tda9950_cec_del(void *data)
{
<------>struct tda9950_priv *priv = data;
<------>cec_delete_adapter(priv->adap);
}
static int tda9950_probe(struct i2c_client *client,
<------><------><------> const struct i2c_device_id *id)
{
<------>struct tda9950_glue *glue = client->dev.platform_data;
<------>struct device *dev = &client->dev;
<------>struct tda9950_priv *priv;
<------>unsigned long irqflags;
<------>int ret;
<------>u8 cvr;
<------>/*
<------> * We must have I2C functionality: our multi-byte accesses
<------> * must be performed as a single contiguous transaction.
<------> */
<------>if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
<------><------>dev_err(&client->dev,
<------><------><------>"adapter does not support I2C functionality\n");
<------><------>return -ENXIO;
<------>}
<------>/* We must have an interrupt to be functional. */
<------>if (client->irq <= 0) {
<------><------>dev_err(&client->dev, "driver requires an interrupt\n");
<------><------>return -ENXIO;
<------>}
<------>priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
<------>if (!priv)
<------><------>return -ENOMEM;
<------>priv->client = client;
<------>priv->glue = glue;
<------>i2c_set_clientdata(client, priv);
<------>/*
<------> * If we're part of a TDA998x, we want the class devices to be
<------> * associated with the HDMI Tx so we have a tight relationship
<------> * between the HDMI interface and the CEC interface.
<------> */
<------>priv->hdmi = dev;
<------>if (glue && glue->parent)
<------><------>priv->hdmi = glue->parent;
<------>priv->adap = cec_allocate_adapter(&tda9950_cec_ops, priv, "tda9950",
<------><------><------><------><------> CEC_CAP_DEFAULTS |
<------><------><------><------><------> CEC_CAP_CONNECTOR_INFO,
<------><------><------><------><------> CEC_MAX_LOG_ADDRS);
<------>if (IS_ERR(priv->adap))
<------><------>return PTR_ERR(priv->adap);
<------>ret = devm_add_action(dev, tda9950_cec_del, priv);
<------>if (ret) {
<------><------>cec_delete_adapter(priv->adap);
<------><------>return ret;
<------>}
<------>ret = tda9950_devm_glue_init(dev, glue);
<------>if (ret)
<------><------>return ret;
<------>ret = tda9950_glue_open(priv);
<------>if (ret)
<------><------>return ret;
<------>cvr = tda9950_read(client, REG_CVR);
<------>dev_info(&client->dev,
<------><------> "TDA9950 CEC interface, hardware version %u.%u\n",
<------><------> cvr >> 4, cvr & 15);
<------>tda9950_glue_release(priv);
<------>irqflags = IRQF_TRIGGER_FALLING;
<------>if (glue)
<------><------>irqflags = glue->irq_flags;
<------>ret = devm_request_threaded_irq(dev, client->irq, NULL, tda9950_irq,
<------><------><------><------><------>irqflags | IRQF_SHARED | IRQF_ONESHOT,
<------><------><------><------><------>dev_name(&client->dev), priv);
<------>if (ret < 0)
<------><------>return ret;
<------>priv->notify = cec_notifier_cec_adap_register(priv->hdmi, NULL,
<------><------><------><------><------><------> priv->adap);
<------>if (!priv->notify)
<------><------>return -ENOMEM;
<------>ret = cec_register_adapter(priv->adap, priv->hdmi);
<------>if (ret < 0) {
<------><------>cec_notifier_cec_adap_unregister(priv->notify, priv->adap);
<------><------>return ret;
<------>}
<------>/*
<------> * CEC documentation says we must not call cec_delete_adapter
<------> * after a successful call to cec_register_adapter().
<------> */
<------>devm_remove_action(dev, tda9950_cec_del, priv);
<------>return 0;
}
static int tda9950_remove(struct i2c_client *client)
{
<------>struct tda9950_priv *priv = i2c_get_clientdata(client);
<------>cec_notifier_cec_adap_unregister(priv->notify, priv->adap);
<------>cec_unregister_adapter(priv->adap);
<------>return 0;
}
static struct i2c_device_id tda9950_ids[] = {
<------>{ "tda9950", 0 },
<------>{ },
};
MODULE_DEVICE_TABLE(i2c, tda9950_ids);
static struct i2c_driver tda9950_driver = {
<------>.probe = tda9950_probe,
<------>.remove = tda9950_remove,
<------>.driver = {
<------><------>.name = "tda9950",
<------>},
<------>.id_table = tda9950_ids,
};
module_i2c_driver(tda9950_driver);
MODULE_AUTHOR("Russell King <rmk+kernel@armlinux.org.uk>");
MODULE_DESCRIPTION("TDA9950/TDA998x Consumer Electronics Control Driver");
MODULE_LICENSE("GPL v2");