root/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hdmi_cec_received_msg
  2. hdmi4_cec_irq
  3. hdmi_cec_clear_tx_fifo
  4. hdmi_cec_clear_rx_fifo
  5. hdmi_cec_adap_enable
  6. hdmi_cec_adap_log_addr
  7. hdmi_cec_adap_transmit
  8. hdmi4_cec_set_phys_addr
  9. hdmi4_cec_init
  10. hdmi4_cec_uninit

   1 /*
   2  * HDMI CEC
   3  *
   4  * Based on the CEC code from hdmi_ti_4xxx_ip.c from Android.
   5  *
   6  * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
   7  * Authors: Yong Zhi
   8  *      Mythri pk <mythripk@ti.com>
   9  *
  10  * Heavily modified to use the linux CEC framework:
  11  *
  12  * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  13  *
  14  * This program is free software; you may redistribute it and/or modify
  15  * it under the terms of the GNU General Public License as published by
  16  * the Free Software Foundation; version 2 of the License.
  17  *
  18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  22  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  23  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  24  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  25  * SOFTWARE.
  26  */
  27 
  28 #include <linux/kernel.h>
  29 #include <linux/err.h>
  30 #include <linux/io.h>
  31 #include <linux/platform_device.h>
  32 #include <linux/slab.h>
  33 
  34 #include "dss.h"
  35 #include "hdmi.h"
  36 #include "hdmi4_core.h"
  37 #include "hdmi4_cec.h"
  38 
  39 /* HDMI CEC */
  40 #define HDMI_CEC_DEV_ID                         0x900
  41 #define HDMI_CEC_SPEC                           0x904
  42 
  43 /* Not really a debug register, more a low-level control register */
  44 #define HDMI_CEC_DBG_3                          0x91C
  45 #define HDMI_CEC_TX_INIT                        0x920
  46 #define HDMI_CEC_TX_DEST                        0x924
  47 #define HDMI_CEC_SETUP                          0x938
  48 #define HDMI_CEC_TX_COMMAND                     0x93C
  49 #define HDMI_CEC_TX_OPERAND                     0x940
  50 #define HDMI_CEC_TRANSMIT_DATA                  0x97C
  51 #define HDMI_CEC_CA_7_0                         0x988
  52 #define HDMI_CEC_CA_15_8                        0x98C
  53 #define HDMI_CEC_INT_STATUS_0                   0x998
  54 #define HDMI_CEC_INT_STATUS_1                   0x99C
  55 #define HDMI_CEC_INT_ENABLE_0                   0x990
  56 #define HDMI_CEC_INT_ENABLE_1                   0x994
  57 #define HDMI_CEC_RX_CONTROL                     0x9B0
  58 #define HDMI_CEC_RX_COUNT                       0x9B4
  59 #define HDMI_CEC_RX_CMD_HEADER                  0x9B8
  60 #define HDMI_CEC_RX_COMMAND                     0x9BC
  61 #define HDMI_CEC_RX_OPERAND                     0x9C0
  62 
  63 #define HDMI_CEC_TX_FIFO_INT_MASK               0x64
  64 #define HDMI_CEC_RETRANSMIT_CNT_INT_MASK        0x2
  65 
  66 #define HDMI_CORE_CEC_RETRY    200
  67 
  68 static void hdmi_cec_received_msg(struct hdmi_core_data *core)
  69 {
  70         u32 cnt = hdmi_read_reg(core->base, HDMI_CEC_RX_COUNT) & 0xff;
  71 
  72         /* While there are CEC frames in the FIFO */
  73         while (cnt & 0x70) {
  74                 /* and the frame doesn't have an error */
  75                 if (!(cnt & 0x80)) {
  76                         struct cec_msg msg = {};
  77                         unsigned int i;
  78 
  79                         /* then read the message */
  80                         msg.len = cnt & 0xf;
  81                         if (msg.len > CEC_MAX_MSG_SIZE - 2)
  82                                 msg.len = CEC_MAX_MSG_SIZE - 2;
  83                         msg.msg[0] = hdmi_read_reg(core->base,
  84                                                    HDMI_CEC_RX_CMD_HEADER);
  85                         msg.msg[1] = hdmi_read_reg(core->base,
  86                                                    HDMI_CEC_RX_COMMAND);
  87                         for (i = 0; i < msg.len; i++) {
  88                                 unsigned int reg = HDMI_CEC_RX_OPERAND + i * 4;
  89 
  90                                 msg.msg[2 + i] =
  91                                         hdmi_read_reg(core->base, reg);
  92                         }
  93                         msg.len += 2;
  94                         cec_received_msg(core->adap, &msg);
  95                 }
  96                 /* Clear the current frame from the FIFO */
  97                 hdmi_write_reg(core->base, HDMI_CEC_RX_CONTROL, 1);
  98                 /* Wait until the current frame is cleared */
  99                 while (hdmi_read_reg(core->base, HDMI_CEC_RX_CONTROL) & 1)
 100                         udelay(1);
 101                 /*
 102                  * Re-read the count register and loop to see if there are
 103                  * more messages in the FIFO.
 104                  */
 105                 cnt = hdmi_read_reg(core->base, HDMI_CEC_RX_COUNT) & 0xff;
 106         }
 107 }
 108 
 109 void hdmi4_cec_irq(struct hdmi_core_data *core)
 110 {
 111         u32 stat0 = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_0);
 112         u32 stat1 = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1);
 113 
 114         hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0, stat0);
 115         hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, stat1);
 116 
 117         if (stat0 & 0x20) {
 118                 cec_transmit_done(core->adap, CEC_TX_STATUS_OK,
 119                                   0, 0, 0, 0);
 120                 REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7);
 121         } else if (stat1 & 0x02) {
 122                 u32 dbg3 = hdmi_read_reg(core->base, HDMI_CEC_DBG_3);
 123 
 124                 cec_transmit_done(core->adap,
 125                                   CEC_TX_STATUS_NACK |
 126                                   CEC_TX_STATUS_MAX_RETRIES,
 127                                   0, (dbg3 >> 4) & 7, 0, 0);
 128                 REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7);
 129         }
 130         if (stat0 & 0x02)
 131                 hdmi_cec_received_msg(core);
 132 }
 133 
 134 static bool hdmi_cec_clear_tx_fifo(struct cec_adapter *adap)
 135 {
 136         struct hdmi_core_data *core = cec_get_drvdata(adap);
 137         int retry = HDMI_CORE_CEC_RETRY;
 138         int temp;
 139 
 140         REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7);
 141         while (retry) {
 142                 temp = hdmi_read_reg(core->base, HDMI_CEC_DBG_3);
 143                 if (FLD_GET(temp, 7, 7) == 0)
 144                         break;
 145                 retry--;
 146         }
 147         return retry != 0;
 148 }
 149 
 150 static bool hdmi_cec_clear_rx_fifo(struct cec_adapter *adap)
 151 {
 152         struct hdmi_core_data *core = cec_get_drvdata(adap);
 153         int retry = HDMI_CORE_CEC_RETRY;
 154         int temp;
 155 
 156         hdmi_write_reg(core->base, HDMI_CEC_RX_CONTROL, 0x3);
 157         retry = HDMI_CORE_CEC_RETRY;
 158         while (retry) {
 159                 temp = hdmi_read_reg(core->base, HDMI_CEC_RX_CONTROL);
 160                 if (FLD_GET(temp, 1, 0) == 0)
 161                         break;
 162                 retry--;
 163         }
 164         return retry != 0;
 165 }
 166 
 167 static int hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
 168 {
 169         struct hdmi_core_data *core = cec_get_drvdata(adap);
 170         int temp, err;
 171 
 172         if (!enable) {
 173                 hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_0, 0);
 174                 hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_1, 0);
 175                 REG_FLD_MOD(core->base, HDMI_CORE_SYS_INTR_UNMASK4, 0, 3, 3);
 176                 hdmi_wp_clear_irqenable(core->wp, HDMI_IRQ_CORE);
 177                 hdmi_wp_set_irqstatus(core->wp, HDMI_IRQ_CORE);
 178                 REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0, 5, 0);
 179                 hdmi4_core_disable(core);
 180                 return 0;
 181         }
 182         err = hdmi4_core_enable(core);
 183         if (err)
 184                 return err;
 185 
 186         /*
 187          * Initialize CEC clock divider: CEC needs 2MHz clock hence
 188          * set the divider to 24 to get 48/24=2MHz clock
 189          */
 190         REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0x18, 5, 0);
 191 
 192         /* Clear TX FIFO */
 193         if (!hdmi_cec_clear_tx_fifo(adap)) {
 194                 pr_err("cec-%s: could not clear TX FIFO\n", adap->name);
 195                 err = -EIO;
 196                 goto err_disable_clk;
 197         }
 198 
 199         /* Clear RX FIFO */
 200         if (!hdmi_cec_clear_rx_fifo(adap)) {
 201                 pr_err("cec-%s: could not clear RX FIFO\n", adap->name);
 202                 err = -EIO;
 203                 goto err_disable_clk;
 204         }
 205 
 206         /* Clear CEC interrupts */
 207         hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1,
 208                 hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1));
 209         hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0,
 210                 hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_0));
 211 
 212         /* Enable HDMI core interrupts */
 213         hdmi_wp_set_irqenable(core->wp, HDMI_IRQ_CORE);
 214         /* Unmask CEC interrupt */
 215         REG_FLD_MOD(core->base, HDMI_CORE_SYS_INTR_UNMASK4, 0x1, 3, 3);
 216         /*
 217          * Enable CEC interrupts:
 218          * Transmit Buffer Full/Empty Change event
 219          * Receiver FIFO Not Empty event
 220          */
 221         hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_0, 0x22);
 222         /*
 223          * Enable CEC interrupts:
 224          * Frame Retransmit Count Exceeded event
 225          */
 226         hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_1, 0x02);
 227 
 228         /* cec calibration enable (self clearing) */
 229         hdmi_write_reg(core->base, HDMI_CEC_SETUP, 0x03);
 230         msleep(20);
 231         hdmi_write_reg(core->base, HDMI_CEC_SETUP, 0x04);
 232 
 233         temp = hdmi_read_reg(core->base, HDMI_CEC_SETUP);
 234         if (FLD_GET(temp, 4, 4) != 0) {
 235                 temp = FLD_MOD(temp, 0, 4, 4);
 236                 hdmi_write_reg(core->base, HDMI_CEC_SETUP, temp);
 237 
 238                 /*
 239                  * If we enabled CEC in middle of a CEC message on the bus,
 240                  * we could have start bit irregularity and/or short
 241                  * pulse event. Clear them now.
 242                  */
 243                 temp = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1);
 244                 temp = FLD_MOD(0x0, 0x5, 2, 0);
 245                 hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, temp);
 246         }
 247         return 0;
 248 
 249 err_disable_clk:
 250         REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0, 5, 0);
 251         hdmi4_core_disable(core);
 252 
 253         return err;
 254 }
 255 
 256 static int hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
 257 {
 258         struct hdmi_core_data *core = cec_get_drvdata(adap);
 259         u32 v;
 260 
 261         if (log_addr == CEC_LOG_ADDR_INVALID) {
 262                 hdmi_write_reg(core->base, HDMI_CEC_CA_7_0, 0);
 263                 hdmi_write_reg(core->base, HDMI_CEC_CA_15_8, 0);
 264                 return 0;
 265         }
 266         if (log_addr <= 7) {
 267                 v = hdmi_read_reg(core->base, HDMI_CEC_CA_7_0);
 268                 v |= 1 << log_addr;
 269                 hdmi_write_reg(core->base, HDMI_CEC_CA_7_0, v);
 270         } else {
 271                 v = hdmi_read_reg(core->base, HDMI_CEC_CA_15_8);
 272                 v |= 1 << (log_addr - 8);
 273                 hdmi_write_reg(core->base, HDMI_CEC_CA_15_8, v);
 274         }
 275         return 0;
 276 }
 277 
 278 static int hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
 279                                    u32 signal_free_time, struct cec_msg *msg)
 280 {
 281         struct hdmi_core_data *core = cec_get_drvdata(adap);
 282         int temp;
 283         u32 i;
 284 
 285         /* Clear TX FIFO */
 286         if (!hdmi_cec_clear_tx_fifo(adap)) {
 287                 pr_err("cec-%s: could not clear TX FIFO for transmit\n",
 288                        adap->name);
 289                 return -EIO;
 290         }
 291 
 292         /* Clear TX interrupts */
 293         hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0,
 294                        HDMI_CEC_TX_FIFO_INT_MASK);
 295 
 296         hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1,
 297                        HDMI_CEC_RETRANSMIT_CNT_INT_MASK);
 298 
 299         /* Set the retry count */
 300         REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, attempts - 1, 6, 4);
 301 
 302         /* Set the initiator addresses */
 303         hdmi_write_reg(core->base, HDMI_CEC_TX_INIT, cec_msg_initiator(msg));
 304 
 305         /* Set destination id */
 306         temp = cec_msg_destination(msg);
 307         if (msg->len == 1)
 308                 temp |= 0x80;
 309         hdmi_write_reg(core->base, HDMI_CEC_TX_DEST, temp);
 310         if (msg->len == 1)
 311                 return 0;
 312 
 313         /* Setup command and arguments for the command */
 314         hdmi_write_reg(core->base, HDMI_CEC_TX_COMMAND, msg->msg[1]);
 315 
 316         for (i = 0; i < msg->len - 2; i++)
 317                 hdmi_write_reg(core->base, HDMI_CEC_TX_OPERAND + i * 4,
 318                                msg->msg[2 + i]);
 319 
 320         /* Operand count */
 321         hdmi_write_reg(core->base, HDMI_CEC_TRANSMIT_DATA,
 322                        (msg->len - 2) | 0x10);
 323         return 0;
 324 }
 325 
 326 static const struct cec_adap_ops hdmi_cec_adap_ops = {
 327         .adap_enable = hdmi_cec_adap_enable,
 328         .adap_log_addr = hdmi_cec_adap_log_addr,
 329         .adap_transmit = hdmi_cec_adap_transmit,
 330 };
 331 
 332 void hdmi4_cec_set_phys_addr(struct hdmi_core_data *core, u16 pa)
 333 {
 334         cec_s_phys_addr(core->adap, pa, false);
 335 }
 336 
 337 int hdmi4_cec_init(struct platform_device *pdev, struct hdmi_core_data *core,
 338                   struct hdmi_wp_data *wp)
 339 {
 340         const u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
 341                          CEC_CAP_PASSTHROUGH | CEC_CAP_RC;
 342         int ret;
 343 
 344         core->adap = cec_allocate_adapter(&hdmi_cec_adap_ops, core,
 345                 "omap4", caps, CEC_MAX_LOG_ADDRS);
 346         ret = PTR_ERR_OR_ZERO(core->adap);
 347         if (ret < 0)
 348                 return ret;
 349         core->wp = wp;
 350 
 351         /* Disable clock initially, hdmi_cec_adap_enable() manages it */
 352         REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0, 5, 0);
 353 
 354         ret = cec_register_adapter(core->adap, &pdev->dev);
 355         if (ret < 0) {
 356                 cec_delete_adapter(core->adap);
 357                 return ret;
 358         }
 359         return 0;
 360 }
 361 
 362 void hdmi4_cec_uninit(struct hdmi_core_data *core)
 363 {
 364         cec_unregister_adapter(core->adap);
 365 }

/* [<][>][^][v][top][bottom][index][help] */