729 lines · c
1/*2 * Driver for Amlogic Meson AO CEC Controller3 *4 * Copyright (C) 2015 Amlogic, Inc. All rights reserved5 * Copyright (C) 2017 BayLibre, SAS6 * Author: Neil Armstrong <narmstrong@baylibre.com>7 *8 * SPDX-License-Identifier: GPL-2.0+9 */10 11#include <linux/bitfield.h>12#include <linux/clk.h>13#include <linux/device.h>14#include <linux/io.h>15#include <linux/delay.h>16#include <linux/kernel.h>17#include <linux/module.h>18#include <linux/of.h>19#include <linux/of_platform.h>20#include <linux/platform_device.h>21#include <linux/types.h>22#include <linux/interrupt.h>23#include <linux/reset.h>24#include <media/cec.h>25#include <media/cec-notifier.h>26 27/* CEC Registers */28 29/*30 * [2:1] cntl_clk31 * - 0 = Disable clk (Power-off mode)32 * - 1 = Enable gated clock (Normal mode)33 * - 2 = Enable free-run clk (Debug mode)34 */35#define CEC_GEN_CNTL_REG 0x0036 37#define CEC_GEN_CNTL_RESET BIT(0)38#define CEC_GEN_CNTL_CLK_DISABLE 039#define CEC_GEN_CNTL_CLK_ENABLE 140#define CEC_GEN_CNTL_CLK_ENABLE_DBG 241#define CEC_GEN_CNTL_CLK_CTRL_MASK GENMASK(2, 1)42 43/*44 * [7:0] cec_reg_addr45 * [15:8] cec_reg_wrdata46 * [16] cec_reg_wr47 * - 0 = Read48 * - 1 = Write49 * [23] bus free50 * [31:24] cec_reg_rddata51 */52#define CEC_RW_REG 0x0453 54#define CEC_RW_ADDR GENMASK(7, 0)55#define CEC_RW_WR_DATA GENMASK(15, 8)56#define CEC_RW_WRITE_EN BIT(16)57#define CEC_RW_BUS_BUSY BIT(23)58#define CEC_RW_RD_DATA GENMASK(31, 24)59 60/*61 * [1] tx intr62 * [2] rx intr63 */64#define CEC_INTR_MASKN_REG 0x0865#define CEC_INTR_CLR_REG 0x0c66#define CEC_INTR_STAT_REG 0x1067 68#define CEC_INTR_TX BIT(1)69#define CEC_INTR_RX BIT(2)70 71/* CEC Commands */72 73#define CEC_TX_MSG_0_HEADER 0x0074#define CEC_TX_MSG_1_OPCODE 0x0175#define CEC_TX_MSG_2_OP1 0x0276#define CEC_TX_MSG_3_OP2 0x0377#define CEC_TX_MSG_4_OP3 0x0478#define CEC_TX_MSG_5_OP4 0x0579#define CEC_TX_MSG_6_OP5 0x0680#define CEC_TX_MSG_7_OP6 0x0781#define CEC_TX_MSG_8_OP7 0x0882#define CEC_TX_MSG_9_OP8 0x0983#define CEC_TX_MSG_A_OP9 0x0A84#define CEC_TX_MSG_B_OP10 0x0B85#define CEC_TX_MSG_C_OP11 0x0C86#define CEC_TX_MSG_D_OP12 0x0D87#define CEC_TX_MSG_E_OP13 0x0E88#define CEC_TX_MSG_F_OP14 0x0F89#define CEC_TX_MSG_LENGTH 0x1090#define CEC_TX_MSG_CMD 0x1191#define CEC_TX_WRITE_BUF 0x1292#define CEC_TX_CLEAR_BUF 0x1393#define CEC_RX_MSG_CMD 0x1494#define CEC_RX_CLEAR_BUF 0x1595#define CEC_LOGICAL_ADDR0 0x1696#define CEC_LOGICAL_ADDR1 0x1797#define CEC_LOGICAL_ADDR2 0x1898#define CEC_LOGICAL_ADDR3 0x1999#define CEC_LOGICAL_ADDR4 0x1A100#define CEC_CLOCK_DIV_H 0x1B101#define CEC_CLOCK_DIV_L 0x1C102#define CEC_QUIESCENT_25MS_BIT7_0 0x20103#define CEC_QUIESCENT_25MS_BIT11_8 0x21104#define CEC_STARTBITMINL2H_3MS5_BIT7_0 0x22105#define CEC_STARTBITMINL2H_3MS5_BIT8 0x23106#define CEC_STARTBITMAXL2H_3MS9_BIT7_0 0x24107#define CEC_STARTBITMAXL2H_3MS9_BIT8 0x25108#define CEC_STARTBITMINH_0MS6_BIT7_0 0x26109#define CEC_STARTBITMINH_0MS6_BIT8 0x27110#define CEC_STARTBITMAXH_1MS0_BIT7_0 0x28111#define CEC_STARTBITMAXH_1MS0_BIT8 0x29112#define CEC_STARTBITMINTOT_4MS3_BIT7_0 0x2A113#define CEC_STARTBITMINTOT_4MS3_BIT9_8 0x2B114#define CEC_STARTBITMAXTOT_4MS7_BIT7_0 0x2C115#define CEC_STARTBITMAXTOT_4MS7_BIT9_8 0x2D116#define CEC_LOGIC1MINL2H_0MS4_BIT7_0 0x2E117#define CEC_LOGIC1MINL2H_0MS4_BIT8 0x2F118#define CEC_LOGIC1MAXL2H_0MS8_BIT7_0 0x30119#define CEC_LOGIC1MAXL2H_0MS8_BIT8 0x31120#define CEC_LOGIC0MINL2H_1MS3_BIT7_0 0x32121#define CEC_LOGIC0MINL2H_1MS3_BIT8 0x33122#define CEC_LOGIC0MAXL2H_1MS7_BIT7_0 0x34123#define CEC_LOGIC0MAXL2H_1MS7_BIT8 0x35124#define CEC_LOGICMINTOTAL_2MS05_BIT7_0 0x36125#define CEC_LOGICMINTOTAL_2MS05_BIT9_8 0x37126#define CEC_LOGICMAXHIGH_2MS8_BIT7_0 0x38127#define CEC_LOGICMAXHIGH_2MS8_BIT8 0x39128#define CEC_LOGICERRLOW_3MS4_BIT7_0 0x3A129#define CEC_LOGICERRLOW_3MS4_BIT8 0x3B130#define CEC_NOMSMPPOINT_1MS05 0x3C131#define CEC_DELCNTR_LOGICERR 0x3E132#define CEC_TXTIME_17MS_BIT7_0 0x40133#define CEC_TXTIME_17MS_BIT10_8 0x41134#define CEC_TXTIME_2BIT_BIT7_0 0x42135#define CEC_TXTIME_2BIT_BIT10_8 0x43136#define CEC_TXTIME_4BIT_BIT7_0 0x44137#define CEC_TXTIME_4BIT_BIT10_8 0x45138#define CEC_STARTBITNOML2H_3MS7_BIT7_0 0x46139#define CEC_STARTBITNOML2H_3MS7_BIT8 0x47140#define CEC_STARTBITNOMH_0MS8_BIT7_0 0x48141#define CEC_STARTBITNOMH_0MS8_BIT8 0x49142#define CEC_LOGIC1NOML2H_0MS6_BIT7_0 0x4A143#define CEC_LOGIC1NOML2H_0MS6_BIT8 0x4B144#define CEC_LOGIC0NOML2H_1MS5_BIT7_0 0x4C145#define CEC_LOGIC0NOML2H_1MS5_BIT8 0x4D146#define CEC_LOGIC1NOMH_1MS8_BIT7_0 0x4E147#define CEC_LOGIC1NOMH_1MS8_BIT8 0x4F148#define CEC_LOGIC0NOMH_0MS9_BIT7_0 0x50149#define CEC_LOGIC0NOMH_0MS9_BIT8 0x51150#define CEC_LOGICERRLOW_3MS6_BIT7_0 0x52151#define CEC_LOGICERRLOW_3MS6_BIT8 0x53152#define CEC_CHKCONTENTION_0MS1 0x54153#define CEC_PREPARENXTBIT_0MS05_BIT7_0 0x56154#define CEC_PREPARENXTBIT_0MS05_BIT8 0x57155#define CEC_NOMSMPACKPOINT_0MS45 0x58156#define CEC_ACK0NOML2H_1MS5_BIT7_0 0x5A157#define CEC_ACK0NOML2H_1MS5_BIT8 0x5B158#define CEC_BUGFIX_DISABLE_0 0x60159#define CEC_BUGFIX_DISABLE_1 0x61160#define CEC_RX_MSG_0_HEADER 0x80161#define CEC_RX_MSG_1_OPCODE 0x81162#define CEC_RX_MSG_2_OP1 0x82163#define CEC_RX_MSG_3_OP2 0x83164#define CEC_RX_MSG_4_OP3 0x84165#define CEC_RX_MSG_5_OP4 0x85166#define CEC_RX_MSG_6_OP5 0x86167#define CEC_RX_MSG_7_OP6 0x87168#define CEC_RX_MSG_8_OP7 0x88169#define CEC_RX_MSG_9_OP8 0x89170#define CEC_RX_MSG_A_OP9 0x8A171#define CEC_RX_MSG_B_OP10 0x8B172#define CEC_RX_MSG_C_OP11 0x8C173#define CEC_RX_MSG_D_OP12 0x8D174#define CEC_RX_MSG_E_OP13 0x8E175#define CEC_RX_MSG_F_OP14 0x8F176#define CEC_RX_MSG_LENGTH 0x90177#define CEC_RX_MSG_STATUS 0x91178#define CEC_RX_NUM_MSG 0x92179#define CEC_TX_MSG_STATUS 0x93180#define CEC_TX_NUM_MSG 0x94181 182 183/* CEC_TX_MSG_CMD definition */184#define TX_NO_OP 0 /* No transaction */185#define TX_REQ_CURRENT 1 /* Transmit earliest message in buffer */186#define TX_ABORT 2 /* Abort transmitting earliest message */187#define TX_REQ_NEXT 3 /* Overwrite earliest msg, transmit next */188 189/* tx_msg_status definition */190#define TX_IDLE 0 /* No transaction */191#define TX_BUSY 1 /* Transmitter is busy */192#define TX_DONE 2 /* Message successfully transmitted */193#define TX_ERROR 3 /* Message transmitted with error */194 195/* rx_msg_cmd */196#define RX_NO_OP 0 /* No transaction */197#define RX_ACK_CURRENT 1 /* Read earliest message in buffer */198#define RX_DISABLE 2 /* Disable receiving latest message */199#define RX_ACK_NEXT 3 /* Clear earliest msg, read next */200 201/* rx_msg_status */202#define RX_IDLE 0 /* No transaction */203#define RX_BUSY 1 /* Receiver is busy */204#define RX_DONE 2 /* Message has been received successfully */205#define RX_ERROR 3 /* Message has been received with error */206 207/* RX_CLEAR_BUF options */208#define CLEAR_START 1209#define CLEAR_STOP 0210 211/* CEC_LOGICAL_ADDRx options */212#define LOGICAL_ADDR_MASK 0xf213#define LOGICAL_ADDR_VALID BIT(4)214#define LOGICAL_ADDR_DISABLE 0215 216#define CEC_CLK_RATE 32768217 218struct meson_ao_cec_device {219 struct platform_device *pdev;220 void __iomem *base;221 struct clk *core;222 spinlock_t cec_reg_lock;223 struct cec_notifier *notify;224 struct cec_adapter *adap;225 struct cec_msg rx_msg;226};227 228#define writel_bits_relaxed(mask, val, addr) \229 writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr)230 231static inline int meson_ao_cec_wait_busy(struct meson_ao_cec_device *ao_cec)232{233 ktime_t timeout = ktime_add_us(ktime_get(), 5000);234 235 while (readl_relaxed(ao_cec->base + CEC_RW_REG) & CEC_RW_BUS_BUSY) {236 if (ktime_compare(ktime_get(), timeout) > 0)237 return -ETIMEDOUT;238 }239 240 return 0;241}242 243static void meson_ao_cec_read(struct meson_ao_cec_device *ao_cec,244 unsigned long address, u8 *data,245 int *res)246{247 unsigned long flags;248 u32 reg = FIELD_PREP(CEC_RW_ADDR, address);249 int ret = 0;250 251 if (res && *res)252 return;253 254 spin_lock_irqsave(&ao_cec->cec_reg_lock, flags);255 256 ret = meson_ao_cec_wait_busy(ao_cec);257 if (ret)258 goto read_out;259 260 writel_relaxed(reg, ao_cec->base + CEC_RW_REG);261 262 ret = meson_ao_cec_wait_busy(ao_cec);263 if (ret)264 goto read_out;265 266 *data = FIELD_GET(CEC_RW_RD_DATA,267 readl_relaxed(ao_cec->base + CEC_RW_REG));268 269read_out:270 spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags);271 272 if (res)273 *res = ret;274}275 276static void meson_ao_cec_write(struct meson_ao_cec_device *ao_cec,277 unsigned long address, u8 data,278 int *res)279{280 unsigned long flags;281 u32 reg = FIELD_PREP(CEC_RW_ADDR, address) |282 FIELD_PREP(CEC_RW_WR_DATA, data) |283 CEC_RW_WRITE_EN;284 int ret = 0;285 286 if (res && *res)287 return;288 289 spin_lock_irqsave(&ao_cec->cec_reg_lock, flags);290 291 ret = meson_ao_cec_wait_busy(ao_cec);292 if (ret)293 goto write_out;294 295 writel_relaxed(reg, ao_cec->base + CEC_RW_REG);296 297write_out:298 spin_unlock_irqrestore(&ao_cec->cec_reg_lock, flags);299 300 if (res)301 *res = ret;302}303 304static inline void meson_ao_cec_irq_setup(struct meson_ao_cec_device *ao_cec,305 bool enable)306{307 u32 cfg = CEC_INTR_TX | CEC_INTR_RX;308 309 writel_bits_relaxed(cfg, enable ? cfg : 0,310 ao_cec->base + CEC_INTR_MASKN_REG);311}312 313static inline int meson_ao_cec_clear(struct meson_ao_cec_device *ao_cec)314{315 int ret = 0;316 317 meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_DISABLE, &ret);318 meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret);319 meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, 1, &ret);320 meson_ao_cec_write(ao_cec, CEC_TX_CLEAR_BUF, 1, &ret);321 if (ret)322 return ret;323 324 udelay(100);325 326 meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, 0, &ret);327 meson_ao_cec_write(ao_cec, CEC_TX_CLEAR_BUF, 0, &ret);328 if (ret)329 return ret;330 331 udelay(100);332 333 meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_NO_OP, &ret);334 meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_NO_OP, &ret);335 336 return ret;337}338 339static int meson_ao_cec_arbit_bit_time_set(struct meson_ao_cec_device *ao_cec,340 unsigned int bit_set,341 unsigned int time_set)342{343 int ret = 0;344 345 switch (bit_set) {346 case CEC_SIGNAL_FREE_TIME_RETRY:347 meson_ao_cec_write(ao_cec, CEC_TXTIME_4BIT_BIT7_0,348 time_set & 0xff, &ret);349 meson_ao_cec_write(ao_cec, CEC_TXTIME_4BIT_BIT10_8,350 (time_set >> 8) & 0x7, &ret);351 break;352 353 case CEC_SIGNAL_FREE_TIME_NEW_INITIATOR:354 meson_ao_cec_write(ao_cec, CEC_TXTIME_2BIT_BIT7_0,355 time_set & 0xff, &ret);356 meson_ao_cec_write(ao_cec, CEC_TXTIME_2BIT_BIT10_8,357 (time_set >> 8) & 0x7, &ret);358 break;359 360 case CEC_SIGNAL_FREE_TIME_NEXT_XFER:361 meson_ao_cec_write(ao_cec, CEC_TXTIME_17MS_BIT7_0,362 time_set & 0xff, &ret);363 meson_ao_cec_write(ao_cec, CEC_TXTIME_17MS_BIT10_8,364 (time_set >> 8) & 0x7, &ret);365 break;366 }367 368 return ret;369}370 371static irqreturn_t meson_ao_cec_irq(int irq, void *data)372{373 struct meson_ao_cec_device *ao_cec = data;374 u32 stat = readl_relaxed(ao_cec->base + CEC_INTR_STAT_REG);375 376 if (stat)377 return IRQ_WAKE_THREAD;378 379 return IRQ_NONE;380}381 382static void meson_ao_cec_irq_tx(struct meson_ao_cec_device *ao_cec)383{384 unsigned long tx_status = 0;385 u8 stat;386 int ret = 0;387 388 meson_ao_cec_read(ao_cec, CEC_TX_MSG_STATUS, &stat, &ret);389 if (ret)390 goto tx_reg_err;391 392 switch (stat) {393 case TX_DONE:394 tx_status = CEC_TX_STATUS_OK;395 break;396 397 case TX_BUSY:398 tx_status = CEC_TX_STATUS_ARB_LOST;399 break;400 401 case TX_IDLE:402 tx_status = CEC_TX_STATUS_LOW_DRIVE;403 break;404 405 case TX_ERROR:406 default:407 tx_status = CEC_TX_STATUS_NACK;408 break;409 }410 411 /* Clear Interruption */412 writel_relaxed(CEC_INTR_TX, ao_cec->base + CEC_INTR_CLR_REG);413 414 /* Stop TX */415 meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_NO_OP, &ret);416 if (ret)417 goto tx_reg_err;418 419 cec_transmit_attempt_done(ao_cec->adap, tx_status);420 return;421 422tx_reg_err:423 cec_transmit_attempt_done(ao_cec->adap, CEC_TX_STATUS_ERROR);424}425 426static void meson_ao_cec_irq_rx(struct meson_ao_cec_device *ao_cec)427{428 int i, ret = 0;429 u8 reg;430 431 meson_ao_cec_read(ao_cec, CEC_RX_MSG_STATUS, ®, &ret);432 if (reg != RX_DONE)433 goto rx_out;434 435 meson_ao_cec_read(ao_cec, CEC_RX_NUM_MSG, ®, &ret);436 if (reg != 1)437 goto rx_out;438 439 meson_ao_cec_read(ao_cec, CEC_RX_MSG_LENGTH, ®, &ret);440 441 ao_cec->rx_msg.len = reg + 1;442 if (ao_cec->rx_msg.len > CEC_MAX_MSG_SIZE)443 ao_cec->rx_msg.len = CEC_MAX_MSG_SIZE;444 445 for (i = 0; i < ao_cec->rx_msg.len; i++) {446 u8 byte;447 448 meson_ao_cec_read(ao_cec, CEC_RX_MSG_0_HEADER + i, &byte, &ret);449 450 ao_cec->rx_msg.msg[i] = byte;451 }452 453 if (ret)454 goto rx_out;455 456 cec_received_msg(ao_cec->adap, &ao_cec->rx_msg);457 458rx_out:459 /* Clear Interruption */460 writel_relaxed(CEC_INTR_RX, ao_cec->base + CEC_INTR_CLR_REG);461 462 /* Ack RX message */463 meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_ACK_CURRENT, &ret);464 meson_ao_cec_write(ao_cec, CEC_RX_MSG_CMD, RX_NO_OP, &ret);465 466 /* Clear RX buffer */467 meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, CLEAR_START, &ret);468 meson_ao_cec_write(ao_cec, CEC_RX_CLEAR_BUF, CLEAR_STOP, &ret);469}470 471static irqreturn_t meson_ao_cec_irq_thread(int irq, void *data)472{473 struct meson_ao_cec_device *ao_cec = data;474 u32 stat = readl_relaxed(ao_cec->base + CEC_INTR_STAT_REG);475 476 if (stat & CEC_INTR_TX)477 meson_ao_cec_irq_tx(ao_cec);478 479 meson_ao_cec_irq_rx(ao_cec);480 481 return IRQ_HANDLED;482}483 484static int meson_ao_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)485{486 struct meson_ao_cec_device *ao_cec = adap->priv;487 int ret = 0;488 489 meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0,490 LOGICAL_ADDR_DISABLE, &ret);491 if (ret)492 return ret;493 494 ret = meson_ao_cec_clear(ao_cec);495 if (ret)496 return ret;497 498 if (logical_addr == CEC_LOG_ADDR_INVALID)499 return 0;500 501 meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0,502 logical_addr & LOGICAL_ADDR_MASK, &ret);503 if (ret)504 return ret;505 506 udelay(100);507 508 meson_ao_cec_write(ao_cec, CEC_LOGICAL_ADDR0,509 (logical_addr & LOGICAL_ADDR_MASK) |510 LOGICAL_ADDR_VALID, &ret);511 512 return ret;513}514 515static int meson_ao_cec_transmit(struct cec_adapter *adap, u8 attempts,516 u32 signal_free_time, struct cec_msg *msg)517{518 struct meson_ao_cec_device *ao_cec = adap->priv;519 int i, ret = 0;520 u8 reg;521 522 meson_ao_cec_read(ao_cec, CEC_TX_MSG_STATUS, ®, &ret);523 if (ret)524 return ret;525 526 if (reg == TX_BUSY) {527 dev_dbg(&ao_cec->pdev->dev, "%s: busy TX: aborting\n",528 __func__);529 meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_ABORT, &ret);530 }531 532 for (i = 0; i < msg->len; i++) {533 meson_ao_cec_write(ao_cec, CEC_TX_MSG_0_HEADER + i,534 msg->msg[i], &ret);535 }536 537 meson_ao_cec_write(ao_cec, CEC_TX_MSG_LENGTH, msg->len - 1, &ret);538 meson_ao_cec_write(ao_cec, CEC_TX_MSG_CMD, TX_REQ_CURRENT, &ret);539 540 return ret;541}542 543static int meson_ao_cec_adap_enable(struct cec_adapter *adap, bool enable)544{545 struct meson_ao_cec_device *ao_cec = adap->priv;546 int ret;547 548 meson_ao_cec_irq_setup(ao_cec, false);549 550 writel_bits_relaxed(CEC_GEN_CNTL_RESET, CEC_GEN_CNTL_RESET,551 ao_cec->base + CEC_GEN_CNTL_REG);552 553 if (!enable)554 return 0;555 556 /* Enable gated clock (Normal mode). */557 writel_bits_relaxed(CEC_GEN_CNTL_CLK_CTRL_MASK,558 FIELD_PREP(CEC_GEN_CNTL_CLK_CTRL_MASK,559 CEC_GEN_CNTL_CLK_ENABLE),560 ao_cec->base + CEC_GEN_CNTL_REG);561 562 udelay(100);563 564 /* Release Reset */565 writel_bits_relaxed(CEC_GEN_CNTL_RESET, 0,566 ao_cec->base + CEC_GEN_CNTL_REG);567 568 /* Clear buffers */569 ret = meson_ao_cec_clear(ao_cec);570 if (ret)571 return ret;572 573 /* CEC arbitration 3/5/7 bit time set. */574 ret = meson_ao_cec_arbit_bit_time_set(ao_cec,575 CEC_SIGNAL_FREE_TIME_RETRY,576 0x118);577 if (ret)578 return ret;579 ret = meson_ao_cec_arbit_bit_time_set(ao_cec,580 CEC_SIGNAL_FREE_TIME_NEW_INITIATOR,581 0x000);582 if (ret)583 return ret;584 ret = meson_ao_cec_arbit_bit_time_set(ao_cec,585 CEC_SIGNAL_FREE_TIME_NEXT_XFER,586 0x2aa);587 if (ret)588 return ret;589 590 meson_ao_cec_irq_setup(ao_cec, true);591 592 return 0;593}594 595static const struct cec_adap_ops meson_ao_cec_ops = {596 .adap_enable = meson_ao_cec_adap_enable,597 .adap_log_addr = meson_ao_cec_set_log_addr,598 .adap_transmit = meson_ao_cec_transmit,599};600 601static int meson_ao_cec_probe(struct platform_device *pdev)602{603 struct meson_ao_cec_device *ao_cec;604 struct device *hdmi_dev;605 int ret, irq;606 607 hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev);608 609 if (IS_ERR(hdmi_dev))610 return PTR_ERR(hdmi_dev);611 612 ao_cec = devm_kzalloc(&pdev->dev, sizeof(*ao_cec), GFP_KERNEL);613 if (!ao_cec)614 return -ENOMEM;615 616 spin_lock_init(&ao_cec->cec_reg_lock);617 618 ao_cec->adap = cec_allocate_adapter(&meson_ao_cec_ops, ao_cec,619 "meson_ao_cec",620 CEC_CAP_DEFAULTS |621 CEC_CAP_CONNECTOR_INFO,622 1); /* Use 1 for now */623 if (IS_ERR(ao_cec->adap))624 return PTR_ERR(ao_cec->adap);625 626 ao_cec->adap->owner = THIS_MODULE;627 628 ao_cec->base = devm_platform_ioremap_resource(pdev, 0);629 if (IS_ERR(ao_cec->base)) {630 ret = PTR_ERR(ao_cec->base);631 goto out_probe_adapter;632 }633 634 irq = platform_get_irq(pdev, 0);635 ret = devm_request_threaded_irq(&pdev->dev, irq,636 meson_ao_cec_irq,637 meson_ao_cec_irq_thread,638 0, NULL, ao_cec);639 if (ret) {640 dev_err(&pdev->dev, "irq request failed\n");641 goto out_probe_adapter;642 }643 644 ao_cec->core = devm_clk_get(&pdev->dev, "core");645 if (IS_ERR(ao_cec->core)) {646 dev_err(&pdev->dev, "core clock request failed\n");647 ret = PTR_ERR(ao_cec->core);648 goto out_probe_adapter;649 }650 651 ret = clk_prepare_enable(ao_cec->core);652 if (ret) {653 dev_err(&pdev->dev, "core clock enable failed\n");654 goto out_probe_adapter;655 }656 657 ret = clk_set_rate(ao_cec->core, CEC_CLK_RATE);658 if (ret) {659 dev_err(&pdev->dev, "core clock set rate failed\n");660 goto out_probe_clk;661 }662 663 device_reset_optional(&pdev->dev);664 665 ao_cec->pdev = pdev;666 platform_set_drvdata(pdev, ao_cec);667 668 ao_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, NULL,669 ao_cec->adap);670 if (!ao_cec->notify) {671 ret = -ENOMEM;672 goto out_probe_clk;673 }674 675 ret = cec_register_adapter(ao_cec->adap, &pdev->dev);676 if (ret < 0)677 goto out_probe_notify;678 679 /* Setup Hardware */680 writel_relaxed(CEC_GEN_CNTL_RESET,681 ao_cec->base + CEC_GEN_CNTL_REG);682 683 return 0;684 685out_probe_notify:686 cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap);687 688out_probe_clk:689 clk_disable_unprepare(ao_cec->core);690 691out_probe_adapter:692 cec_delete_adapter(ao_cec->adap);693 694 dev_err(&pdev->dev, "CEC controller registration failed\n");695 696 return ret;697}698 699static void meson_ao_cec_remove(struct platform_device *pdev)700{701 struct meson_ao_cec_device *ao_cec = platform_get_drvdata(pdev);702 703 clk_disable_unprepare(ao_cec->core);704 705 cec_notifier_cec_adap_unregister(ao_cec->notify, ao_cec->adap);706 cec_unregister_adapter(ao_cec->adap);707}708 709static const struct of_device_id meson_ao_cec_of_match[] = {710 { .compatible = "amlogic,meson-gx-ao-cec", },711 { /* sentinel */ }712};713MODULE_DEVICE_TABLE(of, meson_ao_cec_of_match);714 715static struct platform_driver meson_ao_cec_driver = {716 .probe = meson_ao_cec_probe,717 .remove_new = meson_ao_cec_remove,718 .driver = {719 .name = "meson-ao-cec",720 .of_match_table = meson_ao_cec_of_match,721 },722};723 724module_platform_driver(meson_ao_cec_driver);725 726MODULE_DESCRIPTION("Meson AO CEC Controller driver");727MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");728MODULE_LICENSE("GPL");729