Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * HDMI CEC
0004  *
0005  * Based on the CEC code from hdmi_ti_4xxx_ip.c from Android.
0006  *
0007  * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/
0008  * Authors: Yong Zhi
0009  *  Mythri pk <mythripk@ti.com>
0010  *
0011  * Heavily modified to use the linux CEC framework:
0012  *
0013  * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
0014  */
0015 
0016 #include <linux/kernel.h>
0017 #include <linux/err.h>
0018 #include <linux/io.h>
0019 #include <linux/platform_device.h>
0020 #include <linux/slab.h>
0021 
0022 #include "dss.h"
0023 #include "hdmi.h"
0024 #include "hdmi4_core.h"
0025 #include "hdmi4_cec.h"
0026 
0027 /* HDMI CEC */
0028 #define HDMI_CEC_DEV_ID                         0x900
0029 #define HDMI_CEC_SPEC                           0x904
0030 
0031 /* Not really a debug register, more a low-level control register */
0032 #define HDMI_CEC_DBG_3                          0x91C
0033 #define HDMI_CEC_TX_INIT                        0x920
0034 #define HDMI_CEC_TX_DEST                        0x924
0035 #define HDMI_CEC_SETUP                          0x938
0036 #define HDMI_CEC_TX_COMMAND                     0x93C
0037 #define HDMI_CEC_TX_OPERAND                     0x940
0038 #define HDMI_CEC_TRANSMIT_DATA                  0x97C
0039 #define HDMI_CEC_CA_7_0                         0x988
0040 #define HDMI_CEC_CA_15_8                        0x98C
0041 #define HDMI_CEC_INT_STATUS_0                   0x998
0042 #define HDMI_CEC_INT_STATUS_1                   0x99C
0043 #define HDMI_CEC_INT_ENABLE_0                   0x990
0044 #define HDMI_CEC_INT_ENABLE_1                   0x994
0045 #define HDMI_CEC_RX_CONTROL                     0x9B0
0046 #define HDMI_CEC_RX_COUNT                       0x9B4
0047 #define HDMI_CEC_RX_CMD_HEADER                  0x9B8
0048 #define HDMI_CEC_RX_COMMAND                     0x9BC
0049 #define HDMI_CEC_RX_OPERAND                     0x9C0
0050 
0051 #define HDMI_CEC_TX_FIFO_INT_MASK       0x64
0052 #define HDMI_CEC_RETRANSMIT_CNT_INT_MASK    0x2
0053 
0054 #define HDMI_CORE_CEC_RETRY    200
0055 
0056 static void hdmi_cec_received_msg(struct hdmi_core_data *core)
0057 {
0058     u32 cnt = hdmi_read_reg(core->base, HDMI_CEC_RX_COUNT) & 0xff;
0059 
0060     /* While there are CEC frames in the FIFO */
0061     while (cnt & 0x70) {
0062         /* and the frame doesn't have an error */
0063         if (!(cnt & 0x80)) {
0064             struct cec_msg msg = {};
0065             unsigned int i;
0066 
0067             /* then read the message */
0068             msg.len = cnt & 0xf;
0069             if (msg.len > CEC_MAX_MSG_SIZE - 2)
0070                 msg.len = CEC_MAX_MSG_SIZE - 2;
0071             msg.msg[0] = hdmi_read_reg(core->base,
0072                            HDMI_CEC_RX_CMD_HEADER);
0073             msg.msg[1] = hdmi_read_reg(core->base,
0074                            HDMI_CEC_RX_COMMAND);
0075             for (i = 0; i < msg.len; i++) {
0076                 unsigned int reg = HDMI_CEC_RX_OPERAND + i * 4;
0077 
0078                 msg.msg[2 + i] =
0079                     hdmi_read_reg(core->base, reg);
0080             }
0081             msg.len += 2;
0082             cec_received_msg(core->adap, &msg);
0083         }
0084         /* Clear the current frame from the FIFO */
0085         hdmi_write_reg(core->base, HDMI_CEC_RX_CONTROL, 1);
0086         /* Wait until the current frame is cleared */
0087         while (hdmi_read_reg(core->base, HDMI_CEC_RX_CONTROL) & 1)
0088             udelay(1);
0089         /*
0090          * Re-read the count register and loop to see if there are
0091          * more messages in the FIFO.
0092          */
0093         cnt = hdmi_read_reg(core->base, HDMI_CEC_RX_COUNT) & 0xff;
0094     }
0095 }
0096 
0097 void hdmi4_cec_irq(struct hdmi_core_data *core)
0098 {
0099     u32 stat0 = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_0);
0100     u32 stat1 = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1);
0101 
0102     hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0, stat0);
0103     hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, stat1);
0104 
0105     if (stat0 & 0x20) {
0106         cec_transmit_done(core->adap, CEC_TX_STATUS_OK,
0107                   0, 0, 0, 0);
0108         REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7);
0109     } else if (stat1 & 0x02) {
0110         u32 dbg3 = hdmi_read_reg(core->base, HDMI_CEC_DBG_3);
0111 
0112         cec_transmit_done(core->adap,
0113                   CEC_TX_STATUS_NACK |
0114                   CEC_TX_STATUS_MAX_RETRIES,
0115                   0, (dbg3 >> 4) & 7, 0, 0);
0116         REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7);
0117     }
0118     if (stat0 & 0x02)
0119         hdmi_cec_received_msg(core);
0120 }
0121 
0122 static bool hdmi_cec_clear_tx_fifo(struct cec_adapter *adap)
0123 {
0124     struct hdmi_core_data *core = cec_get_drvdata(adap);
0125     int retry = HDMI_CORE_CEC_RETRY;
0126     int temp;
0127 
0128     REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7);
0129     while (retry) {
0130         temp = hdmi_read_reg(core->base, HDMI_CEC_DBG_3);
0131         if (FLD_GET(temp, 7, 7) == 0)
0132             break;
0133         retry--;
0134     }
0135     return retry != 0;
0136 }
0137 
0138 static bool hdmi_cec_clear_rx_fifo(struct cec_adapter *adap)
0139 {
0140     struct hdmi_core_data *core = cec_get_drvdata(adap);
0141     int retry = HDMI_CORE_CEC_RETRY;
0142     int temp;
0143 
0144     hdmi_write_reg(core->base, HDMI_CEC_RX_CONTROL, 0x3);
0145     retry = HDMI_CORE_CEC_RETRY;
0146     while (retry) {
0147         temp = hdmi_read_reg(core->base, HDMI_CEC_RX_CONTROL);
0148         if (FLD_GET(temp, 1, 0) == 0)
0149             break;
0150         retry--;
0151     }
0152     return retry != 0;
0153 }
0154 
0155 static int hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
0156 {
0157     struct hdmi_core_data *core = cec_get_drvdata(adap);
0158     int temp, err;
0159 
0160     if (!enable) {
0161         hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_0, 0);
0162         hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_1, 0);
0163         REG_FLD_MOD(core->base, HDMI_CORE_SYS_INTR_UNMASK4, 0, 3, 3);
0164         hdmi_wp_clear_irqenable(core->wp, HDMI_IRQ_CORE);
0165         hdmi_wp_set_irqstatus(core->wp, HDMI_IRQ_CORE);
0166         REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0, 5, 0);
0167         hdmi4_core_disable(core);
0168         return 0;
0169     }
0170     err = hdmi4_core_enable(core);
0171     if (err)
0172         return err;
0173 
0174     /*
0175      * Initialize CEC clock divider: CEC needs 2MHz clock hence
0176      * set the divider to 24 to get 48/24=2MHz clock
0177      */
0178     REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0x18, 5, 0);
0179 
0180     /* Clear TX FIFO */
0181     if (!hdmi_cec_clear_tx_fifo(adap)) {
0182         pr_err("cec-%s: could not clear TX FIFO\n", adap->name);
0183         err = -EIO;
0184         goto err_disable_clk;
0185     }
0186 
0187     /* Clear RX FIFO */
0188     if (!hdmi_cec_clear_rx_fifo(adap)) {
0189         pr_err("cec-%s: could not clear RX FIFO\n", adap->name);
0190         err = -EIO;
0191         goto err_disable_clk;
0192     }
0193 
0194     /* Clear CEC interrupts */
0195     hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1,
0196         hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1));
0197     hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0,
0198         hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_0));
0199 
0200     /* Enable HDMI core interrupts */
0201     hdmi_wp_set_irqenable(core->wp, HDMI_IRQ_CORE);
0202     /* Unmask CEC interrupt */
0203     REG_FLD_MOD(core->base, HDMI_CORE_SYS_INTR_UNMASK4, 0x1, 3, 3);
0204     /*
0205      * Enable CEC interrupts:
0206      * Transmit Buffer Full/Empty Change event
0207      * Receiver FIFO Not Empty event
0208      */
0209     hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_0, 0x22);
0210     /*
0211      * Enable CEC interrupts:
0212      * Frame Retransmit Count Exceeded event
0213      */
0214     hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_1, 0x02);
0215 
0216     /* cec calibration enable (self clearing) */
0217     hdmi_write_reg(core->base, HDMI_CEC_SETUP, 0x03);
0218     msleep(20);
0219     hdmi_write_reg(core->base, HDMI_CEC_SETUP, 0x04);
0220 
0221     temp = hdmi_read_reg(core->base, HDMI_CEC_SETUP);
0222     if (FLD_GET(temp, 4, 4) != 0) {
0223         temp = FLD_MOD(temp, 0, 4, 4);
0224         hdmi_write_reg(core->base, HDMI_CEC_SETUP, temp);
0225 
0226         /*
0227          * If we enabled CEC in middle of a CEC message on the bus,
0228          * we could have start bit irregularity and/or short
0229          * pulse event. Clear them now.
0230          */
0231         temp = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1);
0232         temp = FLD_MOD(0x0, 0x5, 2, 0);
0233         hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, temp);
0234     }
0235     return 0;
0236 
0237 err_disable_clk:
0238     REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0, 5, 0);
0239     hdmi4_core_disable(core);
0240 
0241     return err;
0242 }
0243 
0244 static int hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
0245 {
0246     struct hdmi_core_data *core = cec_get_drvdata(adap);
0247     u32 v;
0248 
0249     if (log_addr == CEC_LOG_ADDR_INVALID) {
0250         hdmi_write_reg(core->base, HDMI_CEC_CA_7_0, 0);
0251         hdmi_write_reg(core->base, HDMI_CEC_CA_15_8, 0);
0252         return 0;
0253     }
0254     if (log_addr <= 7) {
0255         v = hdmi_read_reg(core->base, HDMI_CEC_CA_7_0);
0256         v |= 1 << log_addr;
0257         hdmi_write_reg(core->base, HDMI_CEC_CA_7_0, v);
0258     } else {
0259         v = hdmi_read_reg(core->base, HDMI_CEC_CA_15_8);
0260         v |= 1 << (log_addr - 8);
0261         hdmi_write_reg(core->base, HDMI_CEC_CA_15_8, v);
0262     }
0263     return 0;
0264 }
0265 
0266 static int hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
0267                    u32 signal_free_time, struct cec_msg *msg)
0268 {
0269     struct hdmi_core_data *core = cec_get_drvdata(adap);
0270     int temp;
0271     u32 i;
0272 
0273     /* Clear TX FIFO */
0274     if (!hdmi_cec_clear_tx_fifo(adap)) {
0275         pr_err("cec-%s: could not clear TX FIFO for transmit\n",
0276                adap->name);
0277         return -EIO;
0278     }
0279 
0280     /* Clear TX interrupts */
0281     hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0,
0282                HDMI_CEC_TX_FIFO_INT_MASK);
0283 
0284     hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1,
0285                HDMI_CEC_RETRANSMIT_CNT_INT_MASK);
0286 
0287     /* Set the retry count */
0288     REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, attempts - 1, 6, 4);
0289 
0290     /* Set the initiator addresses */
0291     hdmi_write_reg(core->base, HDMI_CEC_TX_INIT, cec_msg_initiator(msg));
0292 
0293     /* Set destination id */
0294     temp = cec_msg_destination(msg);
0295     if (msg->len == 1)
0296         temp |= 0x80;
0297     hdmi_write_reg(core->base, HDMI_CEC_TX_DEST, temp);
0298     if (msg->len == 1)
0299         return 0;
0300 
0301     /* Setup command and arguments for the command */
0302     hdmi_write_reg(core->base, HDMI_CEC_TX_COMMAND, msg->msg[1]);
0303 
0304     for (i = 0; i < msg->len - 2; i++)
0305         hdmi_write_reg(core->base, HDMI_CEC_TX_OPERAND + i * 4,
0306                    msg->msg[2 + i]);
0307 
0308     /* Operand count */
0309     hdmi_write_reg(core->base, HDMI_CEC_TRANSMIT_DATA,
0310                (msg->len - 2) | 0x10);
0311     return 0;
0312 }
0313 
0314 static const struct cec_adap_ops hdmi_cec_adap_ops = {
0315     .adap_enable = hdmi_cec_adap_enable,
0316     .adap_log_addr = hdmi_cec_adap_log_addr,
0317     .adap_transmit = hdmi_cec_adap_transmit,
0318 };
0319 
0320 void hdmi4_cec_set_phys_addr(struct hdmi_core_data *core, u16 pa)
0321 {
0322     cec_s_phys_addr(core->adap, pa, false);
0323 }
0324 
0325 int hdmi4_cec_init(struct platform_device *pdev, struct hdmi_core_data *core,
0326           struct hdmi_wp_data *wp)
0327 {
0328     const u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
0329              CEC_CAP_PASSTHROUGH | CEC_CAP_RC;
0330     int ret;
0331 
0332     core->adap = cec_allocate_adapter(&hdmi_cec_adap_ops, core,
0333         "omap4", caps, CEC_MAX_LOG_ADDRS);
0334     ret = PTR_ERR_OR_ZERO(core->adap);
0335     if (ret < 0)
0336         return ret;
0337     core->wp = wp;
0338 
0339     /* Disable clock initially, hdmi_cec_adap_enable() manages it */
0340     REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0, 5, 0);
0341 
0342     ret = cec_register_adapter(core->adap, &pdev->dev);
0343     if (ret < 0) {
0344         cec_delete_adapter(core->adap);
0345         return ret;
0346     }
0347     return 0;
0348 }
0349 
0350 void hdmi4_cec_uninit(struct hdmi_core_data *core)
0351 {
0352     cec_unregister_adapter(core->adap);
0353 }