brintos

brintos / linux-shallow public Read only

0
0
Text · 18.2 KiB · 494738d Raw
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, &reg, &ret);432	if (reg != RX_DONE)433		goto rx_out;434 435	meson_ao_cec_read(ao_cec, CEC_RX_NUM_MSG, &reg, &ret);436	if (reg != 1)437		goto rx_out;438 439	meson_ao_cec_read(ao_cec, CEC_RX_MSG_LENGTH, &reg, &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, &reg, &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