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