123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484 |
- /*
- * Copyright (c) 2020 Actions Semiconductor Co., Ltd
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- /**
- * @file
- * @brief HDMI CEC driver for Actions SoC
- */
- #include <errno.h>
- #include <kernel.h>
- #include <drivers/cec.h>
- #include <soc.h>
- #include <board_cfg.h>
- #include <logging/log.h>
- LOG_MODULE_REGISTER(cec0, CONFIG_LOG_DEFAULT_LEVEL);
- #ifndef BIT
- #define BIT(n) (1UL << (n))
- #endif
- #define CEC_TIMEOUT_MS (2000) /* transact timeout 2000ms */
- /* cec cr */
- #define CEC_MODE_SHIFT (30)
- #define CEC_MODE_MASK (0x3 << CEC_MODE_SHIFT)
- #define CEC_MODE_ENABLE (1 << CEC_MODE_SHIFT)
- #define CEC_ACKSTATUS BIT(29)
- #define CEC_LOGICAL_ADDR_SHIFT (24)
- #define CEC_LOGICAL_ADDR_MASK (0xF << CEC_LOGICAL_ADDR_SHIFT)
- #define CEC_LOGICAL_ADDR(x) ((x) << CEC_LOGICAL_ADDR_SHIFT)
- #define CEC_TIMER_DIV_SHIFT (16)
- #define CEC_TIMER_DIV_MASK (0xFF << CEC_TIMER_DIV_SHIFT)
- #define CEC_TIMER_DIV(x) ((x) << CEC_TIMER_DIV_SHIFT)
- #define CEC_PRE_DIV_SHIFT (8)
- #define CEC_PRE_DIV_MASK (0xFF << CEC_PRE_DIV_SHIFT)
- #define CEC_PRE_DIV(x) ((x) << CEC_PRE_DIV_SHIFT)
- #define CEC_ACK_MODE BIT(7)
- /* cec rtcr */
- #define CEC_PAD_IN BIT(31)
- #define CEC_WT_CNT_SHIFT (5)
- #define CEC_WT_CNT_MASK (0x3F << CEC_WT_CNT_SHIFT)
- #define CEC_LATTEST BIT(4)
- #define CEC_RETRY_NO_SHIFT (0)
- #define CEC_RETRY_NO_MASK (0xF << CEC_RETRY_NO_SHIFT)
- #define CEC_RETRY_NO(x) ((x) << CEC_RETRY_NO_SHIFT)
- /* cec rxcr */
- #define CEC_RX_MTYPE BIT(16)
- #define CEC_RX_EN BIT(15)
- #define CEC_RX_RST BIT(14)
- #define CEC_RX_CONTINUOUS BIT(13)
- #define CEC_RX_INT_EN BIT(12)
- #define CEC_INIT_ADDR_SHIFT (8)
- #define CEC_INIT_ADDR_MASK (0xF << CEC_INIT_ADDR_SHIFT)
- #define CEC_INIT_ADDR(x) ((x) << CEC_INIT_ADDR_SHIFT)
- #define CEC_RX_EOM BIT(7)
- #define CEC_RX_INT BIT(6)
- #define CEC_RX_FIFO_OV BIT(5)
- #define CEC_RX_FIFO_CNT_SHIFT (0)
- #define CEC_RX_FIFO_CNT_MASK (0x1F << CEC_RX_FIFO_CNT_SHIFT)
- /* cec txcr */
- #define CEC_TX_ADDR_EN BIT(20)
- #define CEC_TX_ADDR_SHIFT (16)
- #define CEC_TX_ADDR_MASK (0xF << CEC_TX_ADDR_SHIFT)
- #define CEC_TX_ADDR(x) ((x) << CEC_TX_ADDR_SHIFT)
- #define CEC_TX_EN BIT(15)
- #define CEC_TX_RST BIT(14)
- #define CEC_TX_CONTINUOUS BIT(13)
- #define CEC_TX_INT_EN BIT(12)
- #define CEC_DEST_ADDR_SHIFT (8)
- #define CEC_DEST_ADDR_MASK (0xF << CEC_DEST_ADDR_SHIFT)
- #define CEC_DEST_ADDR(x) ((x) << CEC_DEST_ADDR_SHIFT)
- #define CEC_TX_EOM BIT(7)
- #define CEC_TX_INT BIT(6)
- #define CEC_TX_FIFO_UD BIT(5)
- #define CEC_TX_FIFO_CNT_SHIFT (0)
- #define CEC_TX_FIFO_CNT_MASK (0x1F << CEC_TX_FIFO_CNT_SHIFT)
- /* cec rxtcr */
- #define CEC_RX_START_LOW_SHIFT (24)
- #define CEC_RX_START_LOW_MASK (0xFF << CEC_RX_START_LOW_SHIFT)
- #define CEC_RX_START_PERIOD_SHIFT (16)
- #define CEC_RX_START_PERIOD_MASK (0xFF << CEC_RX_START_PERIOD_SHIFT)
- #define CEC_RX_DATA_SAMPLE_SHIFT (8)
- #define CEC_RX_DATA_SAMPLE_MASK (0xFF << CEC_RX_DATA_SAMPLE_SHIFT)
- #define CEC_RX_DATA_PERIOD_SHIFT (0)
- #define CEC_RX_DATA_PERIOD_MASK (0xFF << CEC_RX_DATA_PERIOD_SHIFT)
- /* cec txtcr0 */
- #define CEC_TX_START_LOW_SHIFT (8)
- #define CEC_TX_START_LOW_MASK (0xFF << CEC_TX_START_LOW_SHIFT)
- #define CEC_TX_START_HIGH_SHIFT (0)
- #define CEC_TX_START_HIGH_MASK (0xFF << CEC_TX_START_HIGH_SHIFT)
- /* cec txtcr1 */
- #define CEC_TX_DATA_LOW_SHIFT (16)
- #define CEC_TX_DATA_LOW_MASK (0xFF << CEC_TX_DATA_LOW_SHIFT)
- #define CEC_TX_DATA_MID_SHIFT (8)
- #define CEC_TX_DATA_MID_MASK (0xFF << CEC_TX_DATA_01_SHIFT)
- #define CEC_TX_DATA_HIGH_SHIFT (0)
- #define CEC_TX_DATA_HIGH_MASK (0xFF << CEC_TX_DATA_HIGH_SHIFT)
- /* cec pad */
- #define CEC_REG_CEC_ENB BIT(0)
- #define CEC_RETRY_MAX_NUM (5)
- #define CEC_RXTCR_DEFAULT (0x8cbc2a51)
- #define CEC_TXTCR0_DEFAULT (0x9420)
- #define CEC_TXTCR1_DEFAULT (0x182424)
- /* CEC state enumration */
- enum cec_state {
- CEC_STATE_NORMAL = 0,
- CEC_STATE_ERROR
- };
- /* CEC controller */
- struct cec_acts_controller {
- volatile uint32_t cr; /* cec control register */
- volatile uint32_t rtcr; /* cec re-transmission control register */
- volatile uint32_t rxcr; /* cec rx control register */
- volatile uint32_t txcr; /* cec tx control register */
- volatile uint32_t txdr; /* cec tx data register */
- volatile uint32_t rxdr; /* cec rx data register */
- volatile uint32_t rxtcr; /* cec rx timing control register */
- volatile uint32_t txtcr0; /* cec tx timing control register 0 */
- volatile uint32_t txtcr1; /* cec tx timing control register 1 */
- volatile uint32_t pad; /* cec pad register */
- };
- /* cec device configration data */
- struct acts_cec_config {
- struct cec_acts_controller *base;
- void (*irq_config_func)(void);
- uint16_t clock_id;
- uint16_t reset_id;
- uint8_t local_address;
- };
- /* cec driver private data */
- struct acts_cec_data {
- struct k_mutex mutex;
- struct k_sem rx_done;
- struct k_sem tx_done;
- enum cec_state state;
- };
- static void cec_acts_dump_regs(struct cec_acts_controller *cec)
- {
- LOG_INF("CEC base 0x%x:\n"
- " cr: %08x rtcr: %08x rxcr: %08x\n"
- " txcr: %08x txdr: %08x rxdr: %08x\n"
- " rxtcr: %08x txtcr0: %08x txtcr1: %08x\n"
- " pad: %08x\n",
- (unsigned int)cec,
- cec->cr, cec->rtcr, cec->rxcr,
- cec->txcr, cec->txdr, cec->rxdr,
- cec->rxtcr, cec->txtcr0, cec->txtcr1,
- cec->pad);
- }
- static int cec_rx_reset(struct cec_acts_controller *cec)
- {
- /* Write 1 to clear rx state and its FIFO */
- cec->rxcr |= CEC_RX_RST;
- while (cec->rxcr & CEC_RX_RST)
- ;
- return 0;
- }
- static int cec_rx_disable(struct cec_acts_controller *cec)
- {
- /* Disable RX IRQ */
- cec->rxcr &= ~CEC_RX_INT_EN;
- /* Clear RX IRQ pending */
- cec->rxcr |= CEC_RX_INT;
- return cec_rx_reset(cec);
- }
- static int cec_tx_reset(struct cec_acts_controller *cec)
- {
- /* write 1 to clear tx state and its FIFO */
- cec->txcr |= CEC_TX_RST;
- while (cec->txcr & CEC_TX_RST)
- ;
- return 0;
- }
- static int cec_tx_disable(struct cec_acts_controller *cec)
- {
- /* Disable TX IRQ */
- cec->txcr &= ~CEC_TX_INT_EN;
- /* Clear TX IRQ pending */
- cec->txcr |= CEC_TX_INT;
- return cec_tx_reset(cec);
- }
- static int cec_rx_enable(struct cec_acts_controller *cec)
- {
- /* RX and IRQ enable */
- cec->rxcr |= (CEC_RX_EN | CEC_RX_INT_EN);
- /* Clear RX IRQ pending */
- cec->rxcr |= CEC_RX_INT;
- return 0;
- }
- static int cec_tx_enable(struct cec_acts_controller *cec)
- {
- /* TX and IRQ enable */
- cec->txcr |= (CEC_TX_EN | CEC_TX_INT_EN);
- /* Clear TX IRQ pending */
- cec->txcr |= CEC_TX_INT;
- return 0;
- }
- static int cec_set_local_address(const struct device *dev, uint8_t local_addr)
- {
- const struct acts_cec_config *config = dev->config;
- struct cec_acts_controller *cec = config->base;
- /* config initiator address */
- cec->cr &= ~CEC_LOGICAL_ADDR_MASK;
- cec->cr |= CEC_LOGICAL_ADDR(local_addr);
- return 0;
- }
- static void cec_update_state(const struct device *dev, enum cec_state state)
- {
- struct acts_cec_data *data = dev->data;
- data->state = state;
- }
- static int cec_control_init(const struct device *dev)
- {
- const struct acts_cec_config *config = dev->config;
- struct cec_acts_controller *cec = config->base;
- uint32_t reg;
- int ret;
- /* use default value #CEC_RETRY_MAX_NUM to set retry time */
- reg = cec->rtcr & ~CEC_RETRY_NO_MASK;
- cec->rtcr = reg | CEC_RETRY_NO(CEC_RETRY_MAX_NUM);
- cec->rxtcr = CEC_RXTCR_DEFAULT;
- cec->txtcr0 = CEC_TXTCR0_DEFAULT;
- cec->txtcr1 = CEC_TXTCR1_DEFAULT;
- cec->pad = 0;
- /* setup CEC clock 0.8Mhz */
- cec->cr = (cec->cr & ~CEC_PRE_DIV_MASK) | CEC_PRE_DIV(40);
- /* Enable CEC mode */
- cec->cr = (cec->cr & ~CEC_MODE_MASK) | CEC_MODE_ENABLE;
- cec_rx_disable(cec);
- cec_tx_disable(cec);
- /* By defalut to enable cec rx */
- ret = cec_rx_enable(cec);
- /* Setup local address */
- cec_set_local_address(dev, config->local_address);
- if (!ret)
- cec_update_state(dev, CEC_STATE_NORMAL);
- return ret;
- }
- static int acts_cec_write(const struct device *dev, const struct cec_msg *msg, uint32_t timeout_ms)
- {
- const struct acts_cec_config *config = dev->config;
- struct cec_acts_controller *cec = config->base;
- struct acts_cec_data *data = dev->data;
- uint8_t i;
- int ret;
- if ((!msg) || (msg->len > CEC_TRANSFER_MAX_SIZE)
- || (!msg->len)) {
- LOG_ERR("invalid msg");
- return -EINVAL;
- }
- k_mutex_lock(&data->mutex, K_FOREVER);
- cec_update_state(dev, CEC_STATE_NORMAL);
- /* disable rx mode */
- cec_rx_disable(cec);
- cec_tx_reset(cec);
- for (i = 0; i < msg->len; i++)
- cec->txdr = msg->buf[i];
- /* config destination address */
- cec->txcr &= ~CEC_DEST_ADDR_MASK;
- cec->txcr |= CEC_DEST_ADDR(msg->destination);
- /* config initiator address */
- cec_set_local_address(dev, msg->initiator);
- cec_tx_enable(cec);
- /* wait cec tx data transfer is done */
- ret = k_sem_take(&data->tx_done, K_MSEC(timeout_ms));
- if (ret) {
- LOG_ERR("wait tx timeout");
- ret = -EIO;
- goto out;
- }
- if (data->state == CEC_STATE_ERROR) {
- LOG_ERR("cec tx error");
- ret = -EFAULT;
- }
- out:
- if (ret)
- cec_acts_dump_regs(cec);
- /* disable cec tx */
- cec_tx_disable(cec);
- /* enable cec rx */
- cec_rx_enable(cec);
- k_mutex_unlock(&data->mutex);
- return ret;
- }
- static int acts_cec_read(const struct device *dev, struct cec_msg *msg, uint32_t timeout_ms)
- {
- const struct acts_cec_config *config = dev->config;
- struct cec_acts_controller *cec = config->base;
- struct acts_cec_data *data = dev->data;
- uint8_t i, rx_cnt;
- int ret;
- if (!msg) {
- LOG_ERR("invalid msg");
- return -EINVAL;
- }
- k_mutex_lock(&data->mutex, K_FOREVER);
- cec_update_state(dev, CEC_STATE_NORMAL);
- /* wait cec rx data transfer is done */
- ret = k_sem_take(&data->rx_done, K_MSEC(timeout_ms));
- if (ret) {
- LOG_ERR("wait rx timeout");
- ret = -EIO;
- goto out;
- }
- if (data->state == CEC_STATE_ERROR) {
- LOG_ERR("cec rx error");
- ret = -EFAULT;
- goto out;
- }
- msg->initiator = (cec->rxcr & CEC_INIT_ADDR_MASK) >> CEC_INIT_ADDR_SHIFT;
- /* check if a broadcast message */
- if (cec->rxcr & CEC_RX_MTYPE)
- msg->destination = 0xF;
- else
- msg->destination = (cec->cr & CEC_LOGICAL_ADDR_MASK) >> CEC_LOGICAL_ADDR_SHIFT;
- rx_cnt = cec->rxcr & CEC_RX_FIFO_CNT_MASK;
- for (i = 0; i < rx_cnt; i++) {
- if (i >= CEC_TRANSFER_MAX_SIZE) {
- LOG_ERR("invalid rx fifo count %d", rx_cnt);
- ret = -EINVAL;
- goto out;
- }
- msg->buf[i] = cec->rxdr;
- }
- msg->len = rx_cnt;
- out:
- if (ret)
- cec_acts_dump_regs(cec);
- /* rx reset */
- cec_rx_reset(cec);
- /* enable cec rx */
- cec_rx_enable(cec);
- k_mutex_unlock(&data->mutex);
- return ret;
- }
- int acts_cec_init(const struct device *dev)
- {
- const struct acts_cec_config *config = dev->config;
- struct acts_cec_data *data = dev->data;
- /* enable cec controller clock */
- acts_clock_peripheral_enable(config->clock_id);
- /* reset cec controller */
- acts_reset_peripheral(config->reset_id);
- k_mutex_init(&data->mutex);
- k_sem_init(&data->rx_done, 0, 1);
- k_sem_init(&data->tx_done, 0, 1);
- config->irq_config_func();
- cec_control_init(dev);
- printk("cec initialized\n");
- return 0;
- }
- const struct cec_driver_api cec_acts_driver_api = {
- .config = cec_set_local_address,
- .write = acts_cec_write,
- .read = acts_cec_read,
- };
- void acts_cec_isr(void *arg)
- {
- const struct device *dev = (const struct device *)arg;
- const struct acts_cec_config *config = dev->config;
- struct acts_cec_data *data = dev->data;
- struct cec_acts_controller *cec = config->base;
- LOG_DBG("cr:0x%x txcr:0x%x rxcr:0x%x", cec->cr, cec->txcr, cec->rxcr);
- if (cec->txcr & CEC_TX_INT) {
- /* check tx eom */
- if (!(cec->txcr & CEC_TX_EOM))
- data->state = CEC_STATE_ERROR;
- /* check ack status */
- if (!(cec->cr & CEC_ACKSTATUS))
- data->state = CEC_STATE_ERROR;
- cec->txcr |= CEC_TX_INT;
- k_sem_give(&data->tx_done);
- }
- if (cec->rxcr & CEC_RX_INT) {
- /* check rx fifo overflow */
- if (cec->rxcr & CEC_RX_FIFO_OV)
- data->state = CEC_STATE_ERROR;
- cec->rxcr |= CEC_RX_INT;
- k_sem_give(&data->rx_done);
- }
- }
- #if IS_ENABLED(CONFIG_CEC)
- static void cec_acts_config_func_0(void);
- static const struct acts_cec_config acts_cec_config_0 = {
- .base = (struct cec_acts_controller *)CEC_REG_BASE,
- .irq_config_func = cec_acts_config_func_0,
- .clock_id = CLOCK_ID_CEC,
- .reset_id = RESET_ID_CEC,
- .local_address = CONFIG_CEC_0_LOCAL_ADDRESS,
- };
- static struct acts_cec_data acts_cec_data_0;
- #if IS_ENABLED(CONFIG_CEC)
- DEVICE_DEFINE(cec0, CONFIG_CEC_DEV_NAME, &acts_cec_init, NULL,
- &acts_cec_data_0, &acts_cec_config_0,
- POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
- &cec_acts_driver_api);
- #endif
- static void cec_acts_config_func_0(void)
- {
- IRQ_CONNECT(IRQ_ID_CEC, CONFIG_CEC_0_IRQ_PRI,
- acts_cec_isr, DEVICE_GET(cec0), 0);
- irq_enable(IRQ_ID_CEC);
- }
- #endif
|