xref: /openbmc/linux/drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c (revision 03ab8e6297acd1bc0eedaa050e2a1635c576fd11)
1*b94b7353SCai Huoqing // SPDX-License-Identifier: GPL-2.0-only
28d7f934dSHans Verkuil /*
38d7f934dSHans Verkuil  * HDMI CEC
48d7f934dSHans Verkuil  *
58d7f934dSHans Verkuil  * Based on the CEC code from hdmi_ti_4xxx_ip.c from Android.
68d7f934dSHans Verkuil  *
71b409fdaSAlexander A. Klimov  * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/
88d7f934dSHans Verkuil  * Authors: Yong Zhi
98d7f934dSHans Verkuil  *	Mythri pk <mythripk@ti.com>
108d7f934dSHans Verkuil  *
118d7f934dSHans Verkuil  * Heavily modified to use the linux CEC framework:
128d7f934dSHans Verkuil  *
138d7f934dSHans Verkuil  * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
148d7f934dSHans Verkuil  */
158d7f934dSHans Verkuil 
168d7f934dSHans Verkuil #include <linux/kernel.h>
178d7f934dSHans Verkuil #include <linux/err.h>
188d7f934dSHans Verkuil #include <linux/io.h>
198d7f934dSHans Verkuil #include <linux/platform_device.h>
208d7f934dSHans Verkuil #include <linux/slab.h>
218d7f934dSHans Verkuil 
228d7f934dSHans Verkuil #include "dss.h"
238d7f934dSHans Verkuil #include "hdmi.h"
248d7f934dSHans Verkuil #include "hdmi4_core.h"
258d7f934dSHans Verkuil #include "hdmi4_cec.h"
268d7f934dSHans Verkuil 
278d7f934dSHans Verkuil /* HDMI CEC */
288d7f934dSHans Verkuil #define HDMI_CEC_DEV_ID                         0x900
298d7f934dSHans Verkuil #define HDMI_CEC_SPEC                           0x904
308d7f934dSHans Verkuil 
318d7f934dSHans Verkuil /* Not really a debug register, more a low-level control register */
328d7f934dSHans Verkuil #define HDMI_CEC_DBG_3                          0x91C
338d7f934dSHans Verkuil #define HDMI_CEC_TX_INIT                        0x920
348d7f934dSHans Verkuil #define HDMI_CEC_TX_DEST                        0x924
358d7f934dSHans Verkuil #define HDMI_CEC_SETUP                          0x938
368d7f934dSHans Verkuil #define HDMI_CEC_TX_COMMAND                     0x93C
378d7f934dSHans Verkuil #define HDMI_CEC_TX_OPERAND                     0x940
388d7f934dSHans Verkuil #define HDMI_CEC_TRANSMIT_DATA                  0x97C
398d7f934dSHans Verkuil #define HDMI_CEC_CA_7_0                         0x988
408d7f934dSHans Verkuil #define HDMI_CEC_CA_15_8                        0x98C
418d7f934dSHans Verkuil #define HDMI_CEC_INT_STATUS_0                   0x998
428d7f934dSHans Verkuil #define HDMI_CEC_INT_STATUS_1                   0x99C
438d7f934dSHans Verkuil #define HDMI_CEC_INT_ENABLE_0                   0x990
448d7f934dSHans Verkuil #define HDMI_CEC_INT_ENABLE_1                   0x994
458d7f934dSHans Verkuil #define HDMI_CEC_RX_CONTROL                     0x9B0
468d7f934dSHans Verkuil #define HDMI_CEC_RX_COUNT                       0x9B4
478d7f934dSHans Verkuil #define HDMI_CEC_RX_CMD_HEADER                  0x9B8
488d7f934dSHans Verkuil #define HDMI_CEC_RX_COMMAND                     0x9BC
498d7f934dSHans Verkuil #define HDMI_CEC_RX_OPERAND                     0x9C0
508d7f934dSHans Verkuil 
518d7f934dSHans Verkuil #define HDMI_CEC_TX_FIFO_INT_MASK		0x64
528d7f934dSHans Verkuil #define HDMI_CEC_RETRANSMIT_CNT_INT_MASK	0x2
538d7f934dSHans Verkuil 
548d7f934dSHans Verkuil #define HDMI_CORE_CEC_RETRY    200
558d7f934dSHans Verkuil 
hdmi_cec_received_msg(struct hdmi_core_data * core)568d7f934dSHans Verkuil static void hdmi_cec_received_msg(struct hdmi_core_data *core)
578d7f934dSHans Verkuil {
588d7f934dSHans Verkuil 	u32 cnt = hdmi_read_reg(core->base, HDMI_CEC_RX_COUNT) & 0xff;
598d7f934dSHans Verkuil 
608d7f934dSHans Verkuil 	/* While there are CEC frames in the FIFO */
618d7f934dSHans Verkuil 	while (cnt & 0x70) {
628d7f934dSHans Verkuil 		/* and the frame doesn't have an error */
638d7f934dSHans Verkuil 		if (!(cnt & 0x80)) {
648d7f934dSHans Verkuil 			struct cec_msg msg = {};
658d7f934dSHans Verkuil 			unsigned int i;
668d7f934dSHans Verkuil 
678d7f934dSHans Verkuil 			/* then read the message */
688d7f934dSHans Verkuil 			msg.len = cnt & 0xf;
69df29c9dbSHans Verkuil 			if (msg.len > CEC_MAX_MSG_SIZE - 2)
70df29c9dbSHans Verkuil 				msg.len = CEC_MAX_MSG_SIZE - 2;
718d7f934dSHans Verkuil 			msg.msg[0] = hdmi_read_reg(core->base,
728d7f934dSHans Verkuil 						   HDMI_CEC_RX_CMD_HEADER);
738d7f934dSHans Verkuil 			msg.msg[1] = hdmi_read_reg(core->base,
748d7f934dSHans Verkuil 						   HDMI_CEC_RX_COMMAND);
758d7f934dSHans Verkuil 			for (i = 0; i < msg.len; i++) {
768d7f934dSHans Verkuil 				unsigned int reg = HDMI_CEC_RX_OPERAND + i * 4;
778d7f934dSHans Verkuil 
788d7f934dSHans Verkuil 				msg.msg[2 + i] =
798d7f934dSHans Verkuil 					hdmi_read_reg(core->base, reg);
808d7f934dSHans Verkuil 			}
818d7f934dSHans Verkuil 			msg.len += 2;
828d7f934dSHans Verkuil 			cec_received_msg(core->adap, &msg);
838d7f934dSHans Verkuil 		}
848d7f934dSHans Verkuil 		/* Clear the current frame from the FIFO */
858d7f934dSHans Verkuil 		hdmi_write_reg(core->base, HDMI_CEC_RX_CONTROL, 1);
868d7f934dSHans Verkuil 		/* Wait until the current frame is cleared */
878d7f934dSHans Verkuil 		while (hdmi_read_reg(core->base, HDMI_CEC_RX_CONTROL) & 1)
888d7f934dSHans Verkuil 			udelay(1);
898d7f934dSHans Verkuil 		/*
908d7f934dSHans Verkuil 		 * Re-read the count register and loop to see if there are
918d7f934dSHans Verkuil 		 * more messages in the FIFO.
928d7f934dSHans Verkuil 		 */
938d7f934dSHans Verkuil 		cnt = hdmi_read_reg(core->base, HDMI_CEC_RX_COUNT) & 0xff;
948d7f934dSHans Verkuil 	}
958d7f934dSHans Verkuil }
968d7f934dSHans Verkuil 
hdmi4_cec_irq(struct hdmi_core_data * core)978d7f934dSHans Verkuil void hdmi4_cec_irq(struct hdmi_core_data *core)
988d7f934dSHans Verkuil {
998d7f934dSHans Verkuil 	u32 stat0 = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_0);
1008d7f934dSHans Verkuil 	u32 stat1 = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1);
1018d7f934dSHans Verkuil 
1028d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0, stat0);
1038d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, stat1);
1048d7f934dSHans Verkuil 
105df29c9dbSHans Verkuil 	if (stat0 & 0x20) {
106df29c9dbSHans Verkuil 		cec_transmit_done(core->adap, CEC_TX_STATUS_OK,
107df29c9dbSHans Verkuil 				  0, 0, 0, 0);
1088d7f934dSHans Verkuil 		REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7);
109df29c9dbSHans Verkuil 	} else if (stat1 & 0x02) {
1108d7f934dSHans Verkuil 		u32 dbg3 = hdmi_read_reg(core->base, HDMI_CEC_DBG_3);
1118d7f934dSHans Verkuil 
1128d7f934dSHans Verkuil 		cec_transmit_done(core->adap,
1138d7f934dSHans Verkuil 				  CEC_TX_STATUS_NACK |
1148d7f934dSHans Verkuil 				  CEC_TX_STATUS_MAX_RETRIES,
1158d7f934dSHans Verkuil 				  0, (dbg3 >> 4) & 7, 0, 0);
116df29c9dbSHans Verkuil 		REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7);
1178d7f934dSHans Verkuil 	}
1188d7f934dSHans Verkuil 	if (stat0 & 0x02)
1198d7f934dSHans Verkuil 		hdmi_cec_received_msg(core);
1208d7f934dSHans Verkuil }
1218d7f934dSHans Verkuil 
hdmi_cec_clear_tx_fifo(struct cec_adapter * adap)1228d7f934dSHans Verkuil static bool hdmi_cec_clear_tx_fifo(struct cec_adapter *adap)
1238d7f934dSHans Verkuil {
1248d7f934dSHans Verkuil 	struct hdmi_core_data *core = cec_get_drvdata(adap);
1258d7f934dSHans Verkuil 	int retry = HDMI_CORE_CEC_RETRY;
1268d7f934dSHans Verkuil 	int temp;
1278d7f934dSHans Verkuil 
1288d7f934dSHans Verkuil 	REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7);
1298d7f934dSHans Verkuil 	while (retry) {
1308d7f934dSHans Verkuil 		temp = hdmi_read_reg(core->base, HDMI_CEC_DBG_3);
1318d7f934dSHans Verkuil 		if (FLD_GET(temp, 7, 7) == 0)
1328d7f934dSHans Verkuil 			break;
1338d7f934dSHans Verkuil 		retry--;
1348d7f934dSHans Verkuil 	}
1358d7f934dSHans Verkuil 	return retry != 0;
1368d7f934dSHans Verkuil }
1378d7f934dSHans Verkuil 
hdmi_cec_clear_rx_fifo(struct cec_adapter * adap)1388d7f934dSHans Verkuil static bool hdmi_cec_clear_rx_fifo(struct cec_adapter *adap)
1398d7f934dSHans Verkuil {
1408d7f934dSHans Verkuil 	struct hdmi_core_data *core = cec_get_drvdata(adap);
1418d7f934dSHans Verkuil 	int retry = HDMI_CORE_CEC_RETRY;
1428d7f934dSHans Verkuil 	int temp;
1438d7f934dSHans Verkuil 
1448d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_RX_CONTROL, 0x3);
1458d7f934dSHans Verkuil 	retry = HDMI_CORE_CEC_RETRY;
1468d7f934dSHans Verkuil 	while (retry) {
1478d7f934dSHans Verkuil 		temp = hdmi_read_reg(core->base, HDMI_CEC_RX_CONTROL);
1488d7f934dSHans Verkuil 		if (FLD_GET(temp, 1, 0) == 0)
1498d7f934dSHans Verkuil 			break;
1508d7f934dSHans Verkuil 		retry--;
1518d7f934dSHans Verkuil 	}
1528d7f934dSHans Verkuil 	return retry != 0;
1538d7f934dSHans Verkuil }
1548d7f934dSHans Verkuil 
hdmi_cec_adap_enable(struct cec_adapter * adap,bool enable)1558d7f934dSHans Verkuil static int hdmi_cec_adap_enable(struct cec_adapter *adap, bool enable)
1568d7f934dSHans Verkuil {
1578d7f934dSHans Verkuil 	struct hdmi_core_data *core = cec_get_drvdata(adap);
1588d7f934dSHans Verkuil 	int temp, err;
1598d7f934dSHans Verkuil 
1608d7f934dSHans Verkuil 	if (!enable) {
1618d7f934dSHans Verkuil 		hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_0, 0);
1628d7f934dSHans Verkuil 		hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_1, 0);
1638d7f934dSHans Verkuil 		REG_FLD_MOD(core->base, HDMI_CORE_SYS_INTR_UNMASK4, 0, 3, 3);
1648d7f934dSHans Verkuil 		hdmi_wp_clear_irqenable(core->wp, HDMI_IRQ_CORE);
1658d7f934dSHans Verkuil 		hdmi_wp_set_irqstatus(core->wp, HDMI_IRQ_CORE);
16636a1da15STony Lindgren 		REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0, 5, 0);
167ac767456SLaurent Pinchart 		hdmi4_core_disable(core);
1688d7f934dSHans Verkuil 		return 0;
1698d7f934dSHans Verkuil 	}
170ac767456SLaurent Pinchart 	err = hdmi4_core_enable(core);
1718d7f934dSHans Verkuil 	if (err)
1728d7f934dSHans Verkuil 		return err;
1738d7f934dSHans Verkuil 
17436a1da15STony Lindgren 	/*
17536a1da15STony Lindgren 	 * Initialize CEC clock divider: CEC needs 2MHz clock hence
17636a1da15STony Lindgren 	 * set the divider to 24 to get 48/24=2MHz clock
17736a1da15STony Lindgren 	 */
17836a1da15STony Lindgren 	REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0x18, 5, 0);
17936a1da15STony Lindgren 
1808d7f934dSHans Verkuil 	/* Clear TX FIFO */
1818d7f934dSHans Verkuil 	if (!hdmi_cec_clear_tx_fifo(adap)) {
1828d7f934dSHans Verkuil 		pr_err("cec-%s: could not clear TX FIFO\n", adap->name);
18336a1da15STony Lindgren 		err = -EIO;
18436a1da15STony Lindgren 		goto err_disable_clk;
1858d7f934dSHans Verkuil 	}
1868d7f934dSHans Verkuil 
1878d7f934dSHans Verkuil 	/* Clear RX FIFO */
1888d7f934dSHans Verkuil 	if (!hdmi_cec_clear_rx_fifo(adap)) {
1898d7f934dSHans Verkuil 		pr_err("cec-%s: could not clear RX FIFO\n", adap->name);
19036a1da15STony Lindgren 		err = -EIO;
19136a1da15STony Lindgren 		goto err_disable_clk;
1928d7f934dSHans Verkuil 	}
1938d7f934dSHans Verkuil 
1948d7f934dSHans Verkuil 	/* Clear CEC interrupts */
1958d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1,
1968d7f934dSHans Verkuil 		hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1));
1978d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0,
1988d7f934dSHans Verkuil 		hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_0));
1998d7f934dSHans Verkuil 
2008d7f934dSHans Verkuil 	/* Enable HDMI core interrupts */
2018d7f934dSHans Verkuil 	hdmi_wp_set_irqenable(core->wp, HDMI_IRQ_CORE);
2028d7f934dSHans Verkuil 	/* Unmask CEC interrupt */
2038d7f934dSHans Verkuil 	REG_FLD_MOD(core->base, HDMI_CORE_SYS_INTR_UNMASK4, 0x1, 3, 3);
2048d7f934dSHans Verkuil 	/*
2058d7f934dSHans Verkuil 	 * Enable CEC interrupts:
2068d7f934dSHans Verkuil 	 * Transmit Buffer Full/Empty Change event
2078d7f934dSHans Verkuil 	 * Receiver FIFO Not Empty event
2088d7f934dSHans Verkuil 	 */
209df29c9dbSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_0, 0x22);
2108d7f934dSHans Verkuil 	/*
2118d7f934dSHans Verkuil 	 * Enable CEC interrupts:
2128d7f934dSHans Verkuil 	 * Frame Retransmit Count Exceeded event
2138d7f934dSHans Verkuil 	 */
214df29c9dbSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_1, 0x02);
2158d7f934dSHans Verkuil 
2168d7f934dSHans Verkuil 	/* cec calibration enable (self clearing) */
2178d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_SETUP, 0x03);
2188d7f934dSHans Verkuil 	msleep(20);
2198d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_SETUP, 0x04);
2208d7f934dSHans Verkuil 
2218d7f934dSHans Verkuil 	temp = hdmi_read_reg(core->base, HDMI_CEC_SETUP);
2228d7f934dSHans Verkuil 	if (FLD_GET(temp, 4, 4) != 0) {
2238d7f934dSHans Verkuil 		temp = FLD_MOD(temp, 0, 4, 4);
2248d7f934dSHans Verkuil 		hdmi_write_reg(core->base, HDMI_CEC_SETUP, temp);
2258d7f934dSHans Verkuil 
2268d7f934dSHans Verkuil 		/*
2278d7f934dSHans Verkuil 		 * If we enabled CEC in middle of a CEC message on the bus,
2288d7f934dSHans Verkuil 		 * we could have start bit irregularity and/or short
2298d7f934dSHans Verkuil 		 * pulse event. Clear them now.
2308d7f934dSHans Verkuil 		 */
2318d7f934dSHans Verkuil 		temp = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_1);
2328d7f934dSHans Verkuil 		temp = FLD_MOD(0x0, 0x5, 2, 0);
2338d7f934dSHans Verkuil 		hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, temp);
2348d7f934dSHans Verkuil 	}
2358d7f934dSHans Verkuil 	return 0;
23636a1da15STony Lindgren 
23736a1da15STony Lindgren err_disable_clk:
23836a1da15STony Lindgren 	REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0, 5, 0);
23936a1da15STony Lindgren 	hdmi4_core_disable(core);
24036a1da15STony Lindgren 
24136a1da15STony Lindgren 	return err;
2428d7f934dSHans Verkuil }
2438d7f934dSHans Verkuil 
hdmi_cec_adap_log_addr(struct cec_adapter * adap,u8 log_addr)2448d7f934dSHans Verkuil static int hdmi_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
2458d7f934dSHans Verkuil {
2468d7f934dSHans Verkuil 	struct hdmi_core_data *core = cec_get_drvdata(adap);
2478d7f934dSHans Verkuil 	u32 v;
2488d7f934dSHans Verkuil 
2498d7f934dSHans Verkuil 	if (log_addr == CEC_LOG_ADDR_INVALID) {
2508d7f934dSHans Verkuil 		hdmi_write_reg(core->base, HDMI_CEC_CA_7_0, 0);
2518d7f934dSHans Verkuil 		hdmi_write_reg(core->base, HDMI_CEC_CA_15_8, 0);
2528d7f934dSHans Verkuil 		return 0;
2538d7f934dSHans Verkuil 	}
2548d7f934dSHans Verkuil 	if (log_addr <= 7) {
2558d7f934dSHans Verkuil 		v = hdmi_read_reg(core->base, HDMI_CEC_CA_7_0);
2568d7f934dSHans Verkuil 		v |= 1 << log_addr;
2578d7f934dSHans Verkuil 		hdmi_write_reg(core->base, HDMI_CEC_CA_7_0, v);
2588d7f934dSHans Verkuil 	} else {
2598d7f934dSHans Verkuil 		v = hdmi_read_reg(core->base, HDMI_CEC_CA_15_8);
2608d7f934dSHans Verkuil 		v |= 1 << (log_addr - 8);
2618d7f934dSHans Verkuil 		hdmi_write_reg(core->base, HDMI_CEC_CA_15_8, v);
2628d7f934dSHans Verkuil 	}
2638d7f934dSHans Verkuil 	return 0;
2648d7f934dSHans Verkuil }
2658d7f934dSHans Verkuil 
hdmi_cec_adap_transmit(struct cec_adapter * adap,u8 attempts,u32 signal_free_time,struct cec_msg * msg)2668d7f934dSHans Verkuil static int hdmi_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
2678d7f934dSHans Verkuil 				   u32 signal_free_time, struct cec_msg *msg)
2688d7f934dSHans Verkuil {
2698d7f934dSHans Verkuil 	struct hdmi_core_data *core = cec_get_drvdata(adap);
2708d7f934dSHans Verkuil 	int temp;
2718d7f934dSHans Verkuil 	u32 i;
2728d7f934dSHans Verkuil 
2738d7f934dSHans Verkuil 	/* Clear TX FIFO */
2748d7f934dSHans Verkuil 	if (!hdmi_cec_clear_tx_fifo(adap)) {
2758d7f934dSHans Verkuil 		pr_err("cec-%s: could not clear TX FIFO for transmit\n",
2768d7f934dSHans Verkuil 		       adap->name);
2778d7f934dSHans Verkuil 		return -EIO;
2788d7f934dSHans Verkuil 	}
2798d7f934dSHans Verkuil 
2808d7f934dSHans Verkuil 	/* Clear TX interrupts */
2818d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0,
2828d7f934dSHans Verkuil 		       HDMI_CEC_TX_FIFO_INT_MASK);
2838d7f934dSHans Verkuil 
2848d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1,
2858d7f934dSHans Verkuil 		       HDMI_CEC_RETRANSMIT_CNT_INT_MASK);
2868d7f934dSHans Verkuil 
2878d7f934dSHans Verkuil 	/* Set the retry count */
2888d7f934dSHans Verkuil 	REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, attempts - 1, 6, 4);
2898d7f934dSHans Verkuil 
2908d7f934dSHans Verkuil 	/* Set the initiator addresses */
2918d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_TX_INIT, cec_msg_initiator(msg));
2928d7f934dSHans Verkuil 
2938d7f934dSHans Verkuil 	/* Set destination id */
2948d7f934dSHans Verkuil 	temp = cec_msg_destination(msg);
2958d7f934dSHans Verkuil 	if (msg->len == 1)
2968d7f934dSHans Verkuil 		temp |= 0x80;
2978d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_TX_DEST, temp);
2988d7f934dSHans Verkuil 	if (msg->len == 1)
2998d7f934dSHans Verkuil 		return 0;
3008d7f934dSHans Verkuil 
3018d7f934dSHans Verkuil 	/* Setup command and arguments for the command */
3028d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_TX_COMMAND, msg->msg[1]);
3038d7f934dSHans Verkuil 
3048d7f934dSHans Verkuil 	for (i = 0; i < msg->len - 2; i++)
3058d7f934dSHans Verkuil 		hdmi_write_reg(core->base, HDMI_CEC_TX_OPERAND + i * 4,
3068d7f934dSHans Verkuil 			       msg->msg[2 + i]);
3078d7f934dSHans Verkuil 
3088d7f934dSHans Verkuil 	/* Operand count */
3098d7f934dSHans Verkuil 	hdmi_write_reg(core->base, HDMI_CEC_TRANSMIT_DATA,
3108d7f934dSHans Verkuil 		       (msg->len - 2) | 0x10);
3118d7f934dSHans Verkuil 	return 0;
3128d7f934dSHans Verkuil }
3138d7f934dSHans Verkuil 
3148d7f934dSHans Verkuil static const struct cec_adap_ops hdmi_cec_adap_ops = {
3158d7f934dSHans Verkuil 	.adap_enable = hdmi_cec_adap_enable,
3168d7f934dSHans Verkuil 	.adap_log_addr = hdmi_cec_adap_log_addr,
3178d7f934dSHans Verkuil 	.adap_transmit = hdmi_cec_adap_transmit,
3188d7f934dSHans Verkuil };
3198d7f934dSHans Verkuil 
hdmi4_cec_set_phys_addr(struct hdmi_core_data * core,u16 pa)3208d7f934dSHans Verkuil void hdmi4_cec_set_phys_addr(struct hdmi_core_data *core, u16 pa)
3218d7f934dSHans Verkuil {
3228d7f934dSHans Verkuil 	cec_s_phys_addr(core->adap, pa, false);
3238d7f934dSHans Verkuil }
3248d7f934dSHans Verkuil 
hdmi4_cec_init(struct platform_device * pdev,struct hdmi_core_data * core,struct hdmi_wp_data * wp)3258d7f934dSHans Verkuil int hdmi4_cec_init(struct platform_device *pdev, struct hdmi_core_data *core,
3268d7f934dSHans Verkuil 		  struct hdmi_wp_data *wp)
3278d7f934dSHans Verkuil {
3288d7f934dSHans Verkuil 	const u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS |
3298d7f934dSHans Verkuil 			 CEC_CAP_PASSTHROUGH | CEC_CAP_RC;
330bc2aba90SDan Carpenter 	int ret;
3318d7f934dSHans Verkuil 
3328d7f934dSHans Verkuil 	core->adap = cec_allocate_adapter(&hdmi_cec_adap_ops, core,
3338d7f934dSHans Verkuil 		"omap4", caps, CEC_MAX_LOG_ADDRS);
3348d7f934dSHans Verkuil 	ret = PTR_ERR_OR_ZERO(core->adap);
3358d7f934dSHans Verkuil 	if (ret < 0)
3368d7f934dSHans Verkuil 		return ret;
3378d7f934dSHans Verkuil 	core->wp = wp;
3388d7f934dSHans Verkuil 
33936a1da15STony Lindgren 	/* Disable clock initially, hdmi_cec_adap_enable() manages it */
34036a1da15STony Lindgren 	REG_FLD_MOD(core->wp->base, HDMI_WP_CLK, 0, 5, 0);
3418d7f934dSHans Verkuil 
3428d7f934dSHans Verkuil 	ret = cec_register_adapter(core->adap, &pdev->dev);
3438d7f934dSHans Verkuil 	if (ret < 0) {
3448d7f934dSHans Verkuil 		cec_delete_adapter(core->adap);
3458d7f934dSHans Verkuil 		return ret;
3468d7f934dSHans Verkuil 	}
3478d7f934dSHans Verkuil 	return 0;
3488d7f934dSHans Verkuil }
3498d7f934dSHans Verkuil 
hdmi4_cec_uninit(struct hdmi_core_data * core)3508d7f934dSHans Verkuil void hdmi4_cec_uninit(struct hdmi_core_data *core)
3518d7f934dSHans Verkuil {
3528d7f934dSHans Verkuil 	cec_unregister_adapter(core->adap);
3538d7f934dSHans Verkuil }
354