linux/drivers/media/platform/tegra-cec/tegra_cec.c
<<
>>
Prefs
   1/*
   2 * Tegra CEC implementation
   3 *
   4 * The original 3.10 CEC driver using a custom API:
   5 *
   6 * Copyright (c) 2012-2015, NVIDIA CORPORATION.  All rights reserved.
   7 *
   8 * Conversion to the CEC framework and to the mainline kernel:
   9 *
  10 * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
  11 *
  12 * This program is free software; you can redistribute it and/or modify it
  13 * under the terms and conditions of the GNU General Public License,
  14 * version 2, as published by the Free Software Foundation.
  15 *
  16 * This program is distributed in the hope it will be useful, but WITHOUT
  17 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  18 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  19 * more details.
  20 *
  21 * You should have received a copy of the GNU General Public License
  22 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  23 */
  24
  25#include <linux/module.h>
  26#include <linux/kernel.h>
  27#include <linux/err.h>
  28#include <linux/errno.h>
  29#include <linux/interrupt.h>
  30#include <linux/slab.h>
  31#include <linux/io.h>
  32#include <linux/clk.h>
  33#include <linux/delay.h>
  34#include <linux/pm.h>
  35#include <linux/of.h>
  36#include <linux/of_platform.h>
  37#include <linux/platform_device.h>
  38#include <linux/clk/tegra.h>
  39
  40#include <media/cec-notifier.h>
  41
  42#include "tegra_cec.h"
  43
  44#define TEGRA_CEC_NAME "tegra-cec"
  45
  46struct tegra_cec {
  47        struct cec_adapter      *adap;
  48        struct device           *dev;
  49        struct clk              *clk;
  50        void __iomem            *cec_base;
  51        struct cec_notifier     *notifier;
  52        int                     tegra_cec_irq;
  53        bool                    rx_done;
  54        bool                    tx_done;
  55        int                     tx_status;
  56        u8                      rx_buf[CEC_MAX_MSG_SIZE];
  57        u8                      rx_buf_cnt;
  58        u32                     tx_buf[CEC_MAX_MSG_SIZE];
  59        u8                      tx_buf_cur;
  60        u8                      tx_buf_cnt;
  61};
  62
  63static inline u32 cec_read(struct tegra_cec *cec, u32 reg)
  64{
  65        return readl(cec->cec_base + reg);
  66}
  67
  68static inline void cec_write(struct tegra_cec *cec, u32 reg, u32 val)
  69{
  70        writel(val, cec->cec_base + reg);
  71}
  72
  73static void tegra_cec_error_recovery(struct tegra_cec *cec)
  74{
  75        u32 hw_ctrl;
  76
  77        hw_ctrl = cec_read(cec, TEGRA_CEC_HW_CONTROL);
  78        cec_write(cec, TEGRA_CEC_HW_CONTROL, 0);
  79        cec_write(cec, TEGRA_CEC_INT_STAT, 0xffffffff);
  80        cec_write(cec, TEGRA_CEC_HW_CONTROL, hw_ctrl);
  81}
  82
  83static irqreturn_t tegra_cec_irq_thread_handler(int irq, void *data)
  84{
  85        struct device *dev = data;
  86        struct tegra_cec *cec = dev_get_drvdata(dev);
  87
  88        if (cec->tx_done) {
  89                cec_transmit_attempt_done(cec->adap, cec->tx_status);
  90                cec->tx_done = false;
  91        }
  92        if (cec->rx_done) {
  93                struct cec_msg msg = {};
  94
  95                msg.len = cec->rx_buf_cnt;
  96                memcpy(msg.msg, cec->rx_buf, msg.len);
  97                cec_received_msg(cec->adap, &msg);
  98                cec->rx_done = false;
  99                cec->rx_buf_cnt = 0;
 100        }
 101        return IRQ_HANDLED;
 102}
 103
 104static irqreturn_t tegra_cec_irq_handler(int irq, void *data)
 105{
 106        struct device *dev = data;
 107        struct tegra_cec *cec = dev_get_drvdata(dev);
 108        u32 status, mask;
 109
 110        status = cec_read(cec, TEGRA_CEC_INT_STAT);
 111        mask = cec_read(cec, TEGRA_CEC_INT_MASK);
 112
 113        status &= mask;
 114
 115        if (!status)
 116                return IRQ_HANDLED;
 117
 118        if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) {
 119                dev_err(dev, "TX underrun, interrupt timing issue!\n");
 120
 121                tegra_cec_error_recovery(cec);
 122                cec_write(cec, TEGRA_CEC_INT_MASK,
 123                          mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
 124
 125                cec->tx_done = true;
 126                cec->tx_status = CEC_TX_STATUS_ERROR;
 127                return IRQ_WAKE_THREAD;
 128        }
 129
 130        if ((status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) ||
 131                   (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) {
 132                tegra_cec_error_recovery(cec);
 133                cec_write(cec, TEGRA_CEC_INT_MASK,
 134                          mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
 135
 136                cec->tx_done = true;
 137                if (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)
 138                        cec->tx_status = CEC_TX_STATUS_LOW_DRIVE;
 139                else
 140                        cec->tx_status = CEC_TX_STATUS_ARB_LOST;
 141                return IRQ_WAKE_THREAD;
 142        }
 143
 144        if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) {
 145                cec_write(cec, TEGRA_CEC_INT_STAT,
 146                          TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED);
 147
 148                if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) {
 149                        tegra_cec_error_recovery(cec);
 150
 151                        cec->tx_done = true;
 152                        cec->tx_status = CEC_TX_STATUS_NACK;
 153                } else {
 154                        cec->tx_done = true;
 155                        cec->tx_status = CEC_TX_STATUS_OK;
 156                }
 157                return IRQ_WAKE_THREAD;
 158        }
 159
 160        if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD)
 161                dev_warn(dev, "TX NAKed on the fly!\n");
 162
 163        if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) {
 164                if (cec->tx_buf_cur == cec->tx_buf_cnt) {
 165                        cec_write(cec, TEGRA_CEC_INT_MASK,
 166                                  mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
 167                } else {
 168                        cec_write(cec, TEGRA_CEC_TX_REGISTER,
 169                                  cec->tx_buf[cec->tx_buf_cur++]);
 170                        cec_write(cec, TEGRA_CEC_INT_STAT,
 171                                  TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY);
 172                }
 173        }
 174
 175        if (status & TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED) {
 176                cec_write(cec, TEGRA_CEC_INT_STAT,
 177                          TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED);
 178                cec->rx_done = false;
 179                cec->rx_buf_cnt = 0;
 180        }
 181        if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) {
 182                u32 v;
 183
 184                cec_write(cec, TEGRA_CEC_INT_STAT,
 185                          TEGRA_CEC_INT_STAT_RX_REGISTER_FULL);
 186                v = cec_read(cec, TEGRA_CEC_RX_REGISTER);
 187                if (cec->rx_buf_cnt < CEC_MAX_MSG_SIZE)
 188                        cec->rx_buf[cec->rx_buf_cnt++] = v & 0xff;
 189                if (v & TEGRA_CEC_RX_REGISTER_EOM) {
 190                        cec->rx_done = true;
 191                        return IRQ_WAKE_THREAD;
 192                }
 193        }
 194
 195        return IRQ_HANDLED;
 196}
 197
 198static int tegra_cec_adap_enable(struct cec_adapter *adap, bool enable)
 199{
 200        struct tegra_cec *cec = adap->priv;
 201
 202        cec->rx_buf_cnt = 0;
 203        cec->tx_buf_cnt = 0;
 204        cec->tx_buf_cur = 0;
 205
 206        cec_write(cec, TEGRA_CEC_HW_CONTROL, 0);
 207        cec_write(cec, TEGRA_CEC_INT_MASK, 0);
 208        cec_write(cec, TEGRA_CEC_INT_STAT, 0xffffffff);
 209        cec_write(cec, TEGRA_CEC_SW_CONTROL, 0);
 210
 211        if (!enable)
 212                return 0;
 213
 214        cec_write(cec, TEGRA_CEC_INPUT_FILTER, (1U << 31) | 0x20);
 215
 216        cec_write(cec, TEGRA_CEC_RX_TIMING_0,
 217                  (0x7a << TEGRA_CEC_RX_TIM0_START_BIT_MAX_LO_TIME_SHIFT) |
 218                  (0x6d << TEGRA_CEC_RX_TIM0_START_BIT_MIN_LO_TIME_SHIFT) |
 219                  (0x93 << TEGRA_CEC_RX_TIM0_START_BIT_MAX_DURATION_SHIFT) |
 220                  (0x86 << TEGRA_CEC_RX_TIM0_START_BIT_MIN_DURATION_SHIFT));
 221
 222        cec_write(cec, TEGRA_CEC_RX_TIMING_1,
 223                  (0x35 << TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_LO_TIME_SHIFT) |
 224                  (0x21 << TEGRA_CEC_RX_TIM1_DATA_BIT_SAMPLE_TIME_SHIFT) |
 225                  (0x56 << TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_DURATION_SHIFT) |
 226                  (0x40 << TEGRA_CEC_RX_TIM1_DATA_BIT_MIN_DURATION_SHIFT));
 227
 228        cec_write(cec, TEGRA_CEC_RX_TIMING_2,
 229                  (0x50 << TEGRA_CEC_RX_TIM2_END_OF_BLOCK_TIME_SHIFT));
 230
 231        cec_write(cec, TEGRA_CEC_TX_TIMING_0,
 232                  (0x74 << TEGRA_CEC_TX_TIM0_START_BIT_LO_TIME_SHIFT) |
 233                  (0x8d << TEGRA_CEC_TX_TIM0_START_BIT_DURATION_SHIFT) |
 234                  (0x08 << TEGRA_CEC_TX_TIM0_BUS_XITION_TIME_SHIFT) |
 235                  (0x71 << TEGRA_CEC_TX_TIM0_BUS_ERROR_LO_TIME_SHIFT));
 236
 237        cec_write(cec, TEGRA_CEC_TX_TIMING_1,
 238                  (0x2f << TEGRA_CEC_TX_TIM1_LO_DATA_BIT_LO_TIME_SHIFT) |
 239                  (0x13 << TEGRA_CEC_TX_TIM1_HI_DATA_BIT_LO_TIME_SHIFT) |
 240                  (0x4b << TEGRA_CEC_TX_TIM1_DATA_BIT_DURATION_SHIFT) |
 241                  (0x21 << TEGRA_CEC_TX_TIM1_ACK_NAK_BIT_SAMPLE_TIME_SHIFT));
 242
 243        cec_write(cec, TEGRA_CEC_TX_TIMING_2,
 244                  (0x07 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_ADDITIONAL_FRAME_SHIFT) |
 245                  (0x05 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_NEW_FRAME_SHIFT) |
 246                  (0x03 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_RETRY_FRAME_SHIFT));
 247
 248        cec_write(cec, TEGRA_CEC_INT_MASK,
 249                  TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN |
 250                  TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD |
 251                  TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED |
 252                  TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED |
 253                  TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED |
 254                  TEGRA_CEC_INT_MASK_RX_REGISTER_FULL |
 255                  TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED);
 256
 257        cec_write(cec, TEGRA_CEC_HW_CONTROL, TEGRA_CEC_HWCTRL_TX_RX_MODE);
 258        return 0;
 259}
 260
 261static int tegra_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr)
 262{
 263        struct tegra_cec *cec = adap->priv;
 264        u32 state = cec_read(cec, TEGRA_CEC_HW_CONTROL);
 265
 266        if (logical_addr == CEC_LOG_ADDR_INVALID)
 267                state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK;
 268        else
 269                state |= TEGRA_CEC_HWCTRL_RX_LADDR((1 << logical_addr));
 270
 271        cec_write(cec, TEGRA_CEC_HW_CONTROL, state);
 272        return 0;
 273}
 274
 275static int tegra_cec_adap_monitor_all_enable(struct cec_adapter *adap,
 276                                             bool enable)
 277{
 278        struct tegra_cec *cec = adap->priv;
 279        u32 reg = cec_read(cec, TEGRA_CEC_HW_CONTROL);
 280
 281        if (enable)
 282                reg |= TEGRA_CEC_HWCTRL_RX_SNOOP;
 283        else
 284                reg &= ~TEGRA_CEC_HWCTRL_RX_SNOOP;
 285        cec_write(cec, TEGRA_CEC_HW_CONTROL, reg);
 286        return 0;
 287}
 288
 289static int tegra_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
 290                                   u32 signal_free_time_ms, struct cec_msg *msg)
 291{
 292        bool retry_xfer = signal_free_time_ms == CEC_SIGNAL_FREE_TIME_RETRY;
 293        struct tegra_cec *cec = adap->priv;
 294        unsigned int i;
 295        u32 mode = 0;
 296        u32 mask;
 297
 298        if (cec_msg_is_broadcast(msg))
 299                mode = TEGRA_CEC_TX_REG_BCAST;
 300
 301        cec->tx_buf_cur = 0;
 302        cec->tx_buf_cnt = msg->len;
 303
 304        for (i = 0; i < msg->len; i++) {
 305                cec->tx_buf[i] = mode | msg->msg[i];
 306                if (i == 0)
 307                        cec->tx_buf[i] |= TEGRA_CEC_TX_REG_START_BIT;
 308                if (i == msg->len - 1)
 309                        cec->tx_buf[i] |= TEGRA_CEC_TX_REG_EOM;
 310                if (i == 0 && retry_xfer)
 311                        cec->tx_buf[i] |= TEGRA_CEC_TX_REG_RETRY;
 312        }
 313
 314        mask = cec_read(cec, TEGRA_CEC_INT_MASK);
 315        cec_write(cec, TEGRA_CEC_INT_MASK,
 316                  mask | TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY);
 317
 318        return 0;
 319}
 320
 321static const struct cec_adap_ops tegra_cec_ops = {
 322        .adap_enable = tegra_cec_adap_enable,
 323        .adap_log_addr = tegra_cec_adap_log_addr,
 324        .adap_transmit = tegra_cec_adap_transmit,
 325        .adap_monitor_all_enable = tegra_cec_adap_monitor_all_enable,
 326};
 327
 328static int tegra_cec_probe(struct platform_device *pdev)
 329{
 330        struct platform_device *hdmi_dev;
 331        struct device_node *np;
 332        struct tegra_cec *cec;
 333        struct resource *res;
 334        int ret = 0;
 335
 336        np = of_parse_phandle(pdev->dev.of_node, "hdmi-phandle", 0);
 337
 338        if (!np) {
 339                dev_err(&pdev->dev, "Failed to find hdmi node in device tree\n");
 340                return -ENODEV;
 341        }
 342        hdmi_dev = of_find_device_by_node(np);
 343        if (hdmi_dev == NULL)
 344                return -EPROBE_DEFER;
 345
 346        cec = devm_kzalloc(&pdev->dev, sizeof(struct tegra_cec), GFP_KERNEL);
 347
 348        if (!cec)
 349                return -ENOMEM;
 350
 351        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 352
 353        if (!res) {
 354                dev_err(&pdev->dev,
 355                        "Unable to allocate resources for device\n");
 356                return -EBUSY;
 357        }
 358
 359        if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
 360                pdev->name)) {
 361                dev_err(&pdev->dev,
 362                        "Unable to request mem region for device\n");
 363                return -EBUSY;
 364        }
 365
 366        cec->tegra_cec_irq = platform_get_irq(pdev, 0);
 367
 368        if (cec->tegra_cec_irq <= 0)
 369                return -EBUSY;
 370
 371        cec->cec_base = devm_ioremap_nocache(&pdev->dev, res->start,
 372                                             resource_size(res));
 373
 374        if (!cec->cec_base) {
 375                dev_err(&pdev->dev, "Unable to grab IOs for device\n");
 376                return -EBUSY;
 377        }
 378
 379        cec->clk = devm_clk_get(&pdev->dev, "cec");
 380
 381        if (IS_ERR_OR_NULL(cec->clk)) {
 382                dev_err(&pdev->dev, "Can't get clock for CEC\n");
 383                return -ENOENT;
 384        }
 385
 386        clk_prepare_enable(cec->clk);
 387
 388        /* set context info. */
 389        cec->dev = &pdev->dev;
 390
 391        platform_set_drvdata(pdev, cec);
 392
 393        ret = devm_request_threaded_irq(&pdev->dev, cec->tegra_cec_irq,
 394                tegra_cec_irq_handler, tegra_cec_irq_thread_handler,
 395                0, "cec_irq", &pdev->dev);
 396
 397        if (ret) {
 398                dev_err(&pdev->dev,
 399                        "Unable to request interrupt for device\n");
 400                goto clk_error;
 401        }
 402
 403        cec->notifier = cec_notifier_get(&hdmi_dev->dev);
 404        if (!cec->notifier) {
 405                ret = -ENOMEM;
 406                goto clk_error;
 407        }
 408
 409        cec->adap = cec_allocate_adapter(&tegra_cec_ops, cec, TEGRA_CEC_NAME,
 410                        CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL,
 411                        CEC_MAX_LOG_ADDRS);
 412        if (IS_ERR(cec->adap)) {
 413                ret = -ENOMEM;
 414                dev_err(&pdev->dev, "Couldn't create cec adapter\n");
 415                goto cec_error;
 416        }
 417        ret = cec_register_adapter(cec->adap, &pdev->dev);
 418        if (ret) {
 419                dev_err(&pdev->dev, "Couldn't register device\n");
 420                goto cec_error;
 421        }
 422
 423        cec_register_cec_notifier(cec->adap, cec->notifier);
 424
 425        return 0;
 426
 427cec_error:
 428        if (cec->notifier)
 429                cec_notifier_put(cec->notifier);
 430        cec_delete_adapter(cec->adap);
 431clk_error:
 432        clk_disable_unprepare(cec->clk);
 433        return ret;
 434}
 435
 436static int tegra_cec_remove(struct platform_device *pdev)
 437{
 438        struct tegra_cec *cec = platform_get_drvdata(pdev);
 439
 440        clk_disable_unprepare(cec->clk);
 441
 442        cec_unregister_adapter(cec->adap);
 443        cec_notifier_put(cec->notifier);
 444
 445        return 0;
 446}
 447
 448#ifdef CONFIG_PM
 449static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state)
 450{
 451        struct tegra_cec *cec = platform_get_drvdata(pdev);
 452
 453        clk_disable_unprepare(cec->clk);
 454
 455        dev_notice(&pdev->dev, "suspended\n");
 456        return 0;
 457}
 458
 459static int tegra_cec_resume(struct platform_device *pdev)
 460{
 461        struct tegra_cec *cec = platform_get_drvdata(pdev);
 462
 463        dev_notice(&pdev->dev, "Resuming\n");
 464
 465        clk_prepare_enable(cec->clk);
 466
 467        return 0;
 468}
 469#endif
 470
 471static const struct of_device_id tegra_cec_of_match[] = {
 472        { .compatible = "nvidia,tegra114-cec", },
 473        { .compatible = "nvidia,tegra124-cec", },
 474        { .compatible = "nvidia,tegra210-cec", },
 475        {},
 476};
 477
 478static struct platform_driver tegra_cec_driver = {
 479        .driver = {
 480                .name = TEGRA_CEC_NAME,
 481                .of_match_table = of_match_ptr(tegra_cec_of_match),
 482        },
 483        .probe = tegra_cec_probe,
 484        .remove = tegra_cec_remove,
 485
 486#ifdef CONFIG_PM
 487        .suspend = tegra_cec_suspend,
 488        .resume = tegra_cec_resume,
 489#endif
 490};
 491
 492module_platform_driver(tegra_cec_driver);
 493
 494MODULE_DESCRIPTION("Tegra HDMI CEC driver");
 495MODULE_AUTHOR("NVIDIA CORPORATION");
 496MODULE_AUTHOR("Cisco Systems, Inc. and/or its affiliates");
 497MODULE_LICENSE("GPL v2");
 498