1a8106818SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0-or-later
2a8106818SMauro Carvalho Chehab /*
3a8106818SMauro Carvalho Chehab  * RainShadow Tech HDMI CEC driver
4a8106818SMauro Carvalho Chehab  *
5a8106818SMauro Carvalho Chehab  * Copyright 2016 Hans Verkuil <hverkuil@xs4all.nl
6a8106818SMauro Carvalho Chehab  */
7a8106818SMauro Carvalho Chehab 
8a8106818SMauro Carvalho Chehab /*
9a8106818SMauro Carvalho Chehab  * Notes:
10a8106818SMauro Carvalho Chehab  *
11a8106818SMauro Carvalho Chehab  * The higher level protocols are currently disabled. This can be added
12a8106818SMauro Carvalho Chehab  * later, similar to how this is done for the Pulse Eight CEC driver.
13a8106818SMauro Carvalho Chehab  *
14a8106818SMauro Carvalho Chehab  * Documentation of the protocol is available here:
15a8106818SMauro Carvalho Chehab  *
16a8106818SMauro Carvalho Chehab  * http://rainshadowtech.com/doc/HDMICECtoUSBandRS232v2.0.pdf
17a8106818SMauro Carvalho Chehab  */
18a8106818SMauro Carvalho Chehab 
19a8106818SMauro Carvalho Chehab #include <linux/completion.h>
20a8106818SMauro Carvalho Chehab #include <linux/ctype.h>
21a8106818SMauro Carvalho Chehab #include <linux/delay.h>
22a8106818SMauro Carvalho Chehab #include <linux/init.h>
23a8106818SMauro Carvalho Chehab #include <linux/interrupt.h>
24a8106818SMauro Carvalho Chehab #include <linux/kernel.h>
25a8106818SMauro Carvalho Chehab #include <linux/module.h>
26a8106818SMauro Carvalho Chehab #include <linux/serio.h>
27a8106818SMauro Carvalho Chehab #include <linux/slab.h>
28a8106818SMauro Carvalho Chehab #include <linux/spinlock.h>
29a8106818SMauro Carvalho Chehab #include <linux/time.h>
30a8106818SMauro Carvalho Chehab #include <linux/workqueue.h>
31a8106818SMauro Carvalho Chehab 
32a8106818SMauro Carvalho Chehab #include <media/cec.h>
33a8106818SMauro Carvalho Chehab 
34a8106818SMauro Carvalho Chehab MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
35a8106818SMauro Carvalho Chehab MODULE_DESCRIPTION("RainShadow Tech HDMI CEC driver");
36a8106818SMauro Carvalho Chehab MODULE_LICENSE("GPL");
37a8106818SMauro Carvalho Chehab 
38a8106818SMauro Carvalho Chehab #define DATA_SIZE 256
39a8106818SMauro Carvalho Chehab 
40a8106818SMauro Carvalho Chehab struct rain {
41a8106818SMauro Carvalho Chehab 	struct device *dev;
42a8106818SMauro Carvalho Chehab 	struct serio *serio;
43a8106818SMauro Carvalho Chehab 	struct cec_adapter *adap;
44a8106818SMauro Carvalho Chehab 	struct completion cmd_done;
45a8106818SMauro Carvalho Chehab 	struct work_struct work;
46a8106818SMauro Carvalho Chehab 
47a8106818SMauro Carvalho Chehab 	/* Low-level ringbuffer, collecting incoming characters */
48a8106818SMauro Carvalho Chehab 	char buf[DATA_SIZE];
49a8106818SMauro Carvalho Chehab 	unsigned int buf_rd_idx;
50a8106818SMauro Carvalho Chehab 	unsigned int buf_wr_idx;
51a8106818SMauro Carvalho Chehab 	unsigned int buf_len;
52a8106818SMauro Carvalho Chehab 	spinlock_t buf_lock;
53a8106818SMauro Carvalho Chehab 
54a8106818SMauro Carvalho Chehab 	/* command buffer */
55a8106818SMauro Carvalho Chehab 	char cmd[DATA_SIZE];
56a8106818SMauro Carvalho Chehab 	unsigned int cmd_idx;
57a8106818SMauro Carvalho Chehab 	bool cmd_started;
58a8106818SMauro Carvalho Chehab 
59a8106818SMauro Carvalho Chehab 	/* reply to a command, only used to store the firmware version */
60a8106818SMauro Carvalho Chehab 	char cmd_reply[DATA_SIZE];
61a8106818SMauro Carvalho Chehab 
62a8106818SMauro Carvalho Chehab 	struct mutex write_lock;
63a8106818SMauro Carvalho Chehab };
64a8106818SMauro Carvalho Chehab 
rain_process_msg(struct rain * rain)65a8106818SMauro Carvalho Chehab static void rain_process_msg(struct rain *rain)
66a8106818SMauro Carvalho Chehab {
67a8106818SMauro Carvalho Chehab 	struct cec_msg msg = {};
68a8106818SMauro Carvalho Chehab 	const char *cmd = rain->cmd + 3;
69a8106818SMauro Carvalho Chehab 	int stat = -1;
70a8106818SMauro Carvalho Chehab 
71a8106818SMauro Carvalho Chehab 	for (; *cmd; cmd++) {
72a8106818SMauro Carvalho Chehab 		if (!isxdigit(*cmd))
73a8106818SMauro Carvalho Chehab 			continue;
74a8106818SMauro Carvalho Chehab 		if (isxdigit(cmd[0]) && isxdigit(cmd[1])) {
75a8106818SMauro Carvalho Chehab 			if (msg.len == CEC_MAX_MSG_SIZE)
76a8106818SMauro Carvalho Chehab 				break;
77a8106818SMauro Carvalho Chehab 			if (hex2bin(msg.msg + msg.len, cmd, 1))
78a8106818SMauro Carvalho Chehab 				continue;
79a8106818SMauro Carvalho Chehab 			msg.len++;
80a8106818SMauro Carvalho Chehab 			cmd++;
81a8106818SMauro Carvalho Chehab 			continue;
82a8106818SMauro Carvalho Chehab 		}
83a8106818SMauro Carvalho Chehab 		if (!cmd[1])
84a8106818SMauro Carvalho Chehab 			stat = hex_to_bin(cmd[0]);
85a8106818SMauro Carvalho Chehab 		break;
86a8106818SMauro Carvalho Chehab 	}
87a8106818SMauro Carvalho Chehab 
88a8106818SMauro Carvalho Chehab 	if (rain->cmd[0] == 'R') {
89a8106818SMauro Carvalho Chehab 		if (stat == 1 || stat == 2)
90a8106818SMauro Carvalho Chehab 			cec_received_msg(rain->adap, &msg);
91a8106818SMauro Carvalho Chehab 		return;
92a8106818SMauro Carvalho Chehab 	}
93a8106818SMauro Carvalho Chehab 
94a8106818SMauro Carvalho Chehab 	switch (stat) {
95a8106818SMauro Carvalho Chehab 	case 1:
96a8106818SMauro Carvalho Chehab 		cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_OK);
97a8106818SMauro Carvalho Chehab 		break;
98a8106818SMauro Carvalho Chehab 	case 2:
99a8106818SMauro Carvalho Chehab 		cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_NACK);
100a8106818SMauro Carvalho Chehab 		break;
101a8106818SMauro Carvalho Chehab 	default:
102a8106818SMauro Carvalho Chehab 		cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_LOW_DRIVE);
103a8106818SMauro Carvalho Chehab 		break;
104a8106818SMauro Carvalho Chehab 	}
105a8106818SMauro Carvalho Chehab }
106a8106818SMauro Carvalho Chehab 
rain_irq_work_handler(struct work_struct * work)107a8106818SMauro Carvalho Chehab static void rain_irq_work_handler(struct work_struct *work)
108a8106818SMauro Carvalho Chehab {
109a8106818SMauro Carvalho Chehab 	struct rain *rain =
110a8106818SMauro Carvalho Chehab 		container_of(work, struct rain, work);
111a8106818SMauro Carvalho Chehab 
112a8106818SMauro Carvalho Chehab 	while (true) {
113a8106818SMauro Carvalho Chehab 		unsigned long flags;
114a8106818SMauro Carvalho Chehab 		char data;
115a8106818SMauro Carvalho Chehab 
116a8106818SMauro Carvalho Chehab 		spin_lock_irqsave(&rain->buf_lock, flags);
117a8106818SMauro Carvalho Chehab 		if (!rain->buf_len) {
118a8106818SMauro Carvalho Chehab 			spin_unlock_irqrestore(&rain->buf_lock, flags);
119a8106818SMauro Carvalho Chehab 			break;
120a8106818SMauro Carvalho Chehab 		}
121a8106818SMauro Carvalho Chehab 
122a8106818SMauro Carvalho Chehab 		data = rain->buf[rain->buf_rd_idx];
123a8106818SMauro Carvalho Chehab 		rain->buf_len--;
124a8106818SMauro Carvalho Chehab 		rain->buf_rd_idx = (rain->buf_rd_idx + 1) & 0xff;
125a8106818SMauro Carvalho Chehab 
126a8106818SMauro Carvalho Chehab 		spin_unlock_irqrestore(&rain->buf_lock, flags);
127a8106818SMauro Carvalho Chehab 
128a8106818SMauro Carvalho Chehab 		if (!rain->cmd_started && data != '?')
129a8106818SMauro Carvalho Chehab 			continue;
130a8106818SMauro Carvalho Chehab 
131a8106818SMauro Carvalho Chehab 		switch (data) {
132a8106818SMauro Carvalho Chehab 		case '\r':
133a8106818SMauro Carvalho Chehab 			rain->cmd[rain->cmd_idx] = '\0';
134a8106818SMauro Carvalho Chehab 			dev_dbg(rain->dev, "received: %s\n", rain->cmd);
135a8106818SMauro Carvalho Chehab 			if (!memcmp(rain->cmd, "REC", 3) ||
136a8106818SMauro Carvalho Chehab 			    !memcmp(rain->cmd, "STA", 3)) {
137a8106818SMauro Carvalho Chehab 				rain_process_msg(rain);
138a8106818SMauro Carvalho Chehab 			} else {
139a8106818SMauro Carvalho Chehab 				strscpy(rain->cmd_reply, rain->cmd,
140a8106818SMauro Carvalho Chehab 					sizeof(rain->cmd_reply));
141a8106818SMauro Carvalho Chehab 				complete(&rain->cmd_done);
142a8106818SMauro Carvalho Chehab 			}
143a8106818SMauro Carvalho Chehab 			rain->cmd_idx = 0;
144a8106818SMauro Carvalho Chehab 			rain->cmd_started = false;
145a8106818SMauro Carvalho Chehab 			break;
146a8106818SMauro Carvalho Chehab 
147a8106818SMauro Carvalho Chehab 		case '\n':
148a8106818SMauro Carvalho Chehab 			rain->cmd_idx = 0;
149a8106818SMauro Carvalho Chehab 			rain->cmd_started = false;
150a8106818SMauro Carvalho Chehab 			break;
151a8106818SMauro Carvalho Chehab 
152a8106818SMauro Carvalho Chehab 		case '?':
153a8106818SMauro Carvalho Chehab 			rain->cmd_idx = 0;
154a8106818SMauro Carvalho Chehab 			rain->cmd_started = true;
155a8106818SMauro Carvalho Chehab 			break;
156a8106818SMauro Carvalho Chehab 
157a8106818SMauro Carvalho Chehab 		default:
158a8106818SMauro Carvalho Chehab 			if (rain->cmd_idx >= DATA_SIZE - 1) {
159a8106818SMauro Carvalho Chehab 				dev_dbg(rain->dev,
160a8106818SMauro Carvalho Chehab 					"throwing away %d bytes of garbage\n", rain->cmd_idx);
161a8106818SMauro Carvalho Chehab 				rain->cmd_idx = 0;
162a8106818SMauro Carvalho Chehab 			}
163a8106818SMauro Carvalho Chehab 			rain->cmd[rain->cmd_idx++] = data;
164a8106818SMauro Carvalho Chehab 			break;
165a8106818SMauro Carvalho Chehab 		}
166a8106818SMauro Carvalho Chehab 	}
167a8106818SMauro Carvalho Chehab }
168a8106818SMauro Carvalho Chehab 
rain_interrupt(struct serio * serio,unsigned char data,unsigned int flags)169a8106818SMauro Carvalho Chehab static irqreturn_t rain_interrupt(struct serio *serio, unsigned char data,
170a8106818SMauro Carvalho Chehab 				    unsigned int flags)
171a8106818SMauro Carvalho Chehab {
172a8106818SMauro Carvalho Chehab 	struct rain *rain = serio_get_drvdata(serio);
173a8106818SMauro Carvalho Chehab 
174a8106818SMauro Carvalho Chehab 	if (rain->buf_len == DATA_SIZE) {
175a8106818SMauro Carvalho Chehab 		dev_warn_once(rain->dev, "buffer overflow\n");
176a8106818SMauro Carvalho Chehab 		return IRQ_HANDLED;
177a8106818SMauro Carvalho Chehab 	}
178a8106818SMauro Carvalho Chehab 	spin_lock(&rain->buf_lock);
179a8106818SMauro Carvalho Chehab 	rain->buf_len++;
180a8106818SMauro Carvalho Chehab 	rain->buf[rain->buf_wr_idx] = data;
181a8106818SMauro Carvalho Chehab 	rain->buf_wr_idx = (rain->buf_wr_idx + 1) & 0xff;
182a8106818SMauro Carvalho Chehab 	spin_unlock(&rain->buf_lock);
183a8106818SMauro Carvalho Chehab 	schedule_work(&rain->work);
184a8106818SMauro Carvalho Chehab 	return IRQ_HANDLED;
185a8106818SMauro Carvalho Chehab }
186a8106818SMauro Carvalho Chehab 
rain_disconnect(struct serio * serio)187a8106818SMauro Carvalho Chehab static void rain_disconnect(struct serio *serio)
188a8106818SMauro Carvalho Chehab {
189a8106818SMauro Carvalho Chehab 	struct rain *rain = serio_get_drvdata(serio);
190a8106818SMauro Carvalho Chehab 
191a8106818SMauro Carvalho Chehab 	cancel_work_sync(&rain->work);
192a8106818SMauro Carvalho Chehab 	cec_unregister_adapter(rain->adap);
193a8106818SMauro Carvalho Chehab 	dev_info(&serio->dev, "disconnected\n");
194a8106818SMauro Carvalho Chehab 	serio_close(serio);
195a8106818SMauro Carvalho Chehab 	serio_set_drvdata(serio, NULL);
196a8106818SMauro Carvalho Chehab 	kfree(rain);
197a8106818SMauro Carvalho Chehab }
198a8106818SMauro Carvalho Chehab 
rain_send(struct rain * rain,const char * command)199a8106818SMauro Carvalho Chehab static int rain_send(struct rain *rain, const char *command)
200a8106818SMauro Carvalho Chehab {
201a8106818SMauro Carvalho Chehab 	int err = serio_write(rain->serio, '!');
202a8106818SMauro Carvalho Chehab 
203a8106818SMauro Carvalho Chehab 	dev_dbg(rain->dev, "send: %s\n", command);
204a8106818SMauro Carvalho Chehab 	while (!err && *command)
205a8106818SMauro Carvalho Chehab 		err = serio_write(rain->serio, *command++);
206a8106818SMauro Carvalho Chehab 	if (!err)
207a8106818SMauro Carvalho Chehab 		err = serio_write(rain->serio, '~');
208a8106818SMauro Carvalho Chehab 
209a8106818SMauro Carvalho Chehab 	return err;
210a8106818SMauro Carvalho Chehab }
211a8106818SMauro Carvalho Chehab 
rain_send_and_wait(struct rain * rain,const char * cmd,const char * reply)212a8106818SMauro Carvalho Chehab static int rain_send_and_wait(struct rain *rain,
213a8106818SMauro Carvalho Chehab 			      const char *cmd, const char *reply)
214a8106818SMauro Carvalho Chehab {
215a8106818SMauro Carvalho Chehab 	int err;
216a8106818SMauro Carvalho Chehab 
217a8106818SMauro Carvalho Chehab 	init_completion(&rain->cmd_done);
218a8106818SMauro Carvalho Chehab 
219a8106818SMauro Carvalho Chehab 	mutex_lock(&rain->write_lock);
220a8106818SMauro Carvalho Chehab 	err = rain_send(rain, cmd);
221a8106818SMauro Carvalho Chehab 	if (err)
222a8106818SMauro Carvalho Chehab 		goto err;
223a8106818SMauro Carvalho Chehab 
224a8106818SMauro Carvalho Chehab 	if (!wait_for_completion_timeout(&rain->cmd_done, HZ)) {
225a8106818SMauro Carvalho Chehab 		err = -ETIMEDOUT;
226a8106818SMauro Carvalho Chehab 		goto err;
227a8106818SMauro Carvalho Chehab 	}
228a8106818SMauro Carvalho Chehab 	if (reply && strncmp(rain->cmd_reply, reply, strlen(reply))) {
229a8106818SMauro Carvalho Chehab 		dev_dbg(rain->dev,
230a8106818SMauro Carvalho Chehab 			 "transmit of '%s': received '%s' instead of '%s'\n",
231a8106818SMauro Carvalho Chehab 			 cmd, rain->cmd_reply, reply);
232a8106818SMauro Carvalho Chehab 		err = -EIO;
233a8106818SMauro Carvalho Chehab 	}
234a8106818SMauro Carvalho Chehab err:
235a8106818SMauro Carvalho Chehab 	mutex_unlock(&rain->write_lock);
236a8106818SMauro Carvalho Chehab 	return err;
237a8106818SMauro Carvalho Chehab }
238a8106818SMauro Carvalho Chehab 
rain_setup(struct rain * rain,struct serio * serio,struct cec_log_addrs * log_addrs,u16 * pa)239a8106818SMauro Carvalho Chehab static int rain_setup(struct rain *rain, struct serio *serio,
240a8106818SMauro Carvalho Chehab 			struct cec_log_addrs *log_addrs, u16 *pa)
241a8106818SMauro Carvalho Chehab {
242a8106818SMauro Carvalho Chehab 	int err;
243a8106818SMauro Carvalho Chehab 
244a8106818SMauro Carvalho Chehab 	err = rain_send_and_wait(rain, "R", "REV");
245a8106818SMauro Carvalho Chehab 	if (err)
246a8106818SMauro Carvalho Chehab 		return err;
247a8106818SMauro Carvalho Chehab 	dev_info(rain->dev, "Firmware version %s\n", rain->cmd_reply + 4);
248a8106818SMauro Carvalho Chehab 
249a8106818SMauro Carvalho Chehab 	err = rain_send_and_wait(rain, "Q 1", "QTY");
250a8106818SMauro Carvalho Chehab 	if (err)
251a8106818SMauro Carvalho Chehab 		return err;
252a8106818SMauro Carvalho Chehab 	err = rain_send_and_wait(rain, "c0000", "CFG");
253a8106818SMauro Carvalho Chehab 	if (err)
254a8106818SMauro Carvalho Chehab 		return err;
255a8106818SMauro Carvalho Chehab 	return rain_send_and_wait(rain, "A F 0000", "ADR");
256a8106818SMauro Carvalho Chehab }
257a8106818SMauro Carvalho Chehab 
rain_cec_adap_enable(struct cec_adapter * adap,bool enable)258a8106818SMauro Carvalho Chehab static int rain_cec_adap_enable(struct cec_adapter *adap, bool enable)
259a8106818SMauro Carvalho Chehab {
260a8106818SMauro Carvalho Chehab 	return 0;
261a8106818SMauro Carvalho Chehab }
262a8106818SMauro Carvalho Chehab 
rain_cec_adap_log_addr(struct cec_adapter * adap,u8 log_addr)263a8106818SMauro Carvalho Chehab static int rain_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
264a8106818SMauro Carvalho Chehab {
265a8106818SMauro Carvalho Chehab 	struct rain *rain = cec_get_drvdata(adap);
266a8106818SMauro Carvalho Chehab 	u8 cmd[16];
267a8106818SMauro Carvalho Chehab 
268a8106818SMauro Carvalho Chehab 	if (log_addr == CEC_LOG_ADDR_INVALID)
269a8106818SMauro Carvalho Chehab 		log_addr = CEC_LOG_ADDR_UNREGISTERED;
270a8106818SMauro Carvalho Chehab 	snprintf(cmd, sizeof(cmd), "A %x", log_addr);
271a8106818SMauro Carvalho Chehab 	return rain_send_and_wait(rain, cmd, "ADR");
272a8106818SMauro Carvalho Chehab }
273a8106818SMauro Carvalho Chehab 
rain_cec_adap_transmit(struct cec_adapter * adap,u8 attempts,u32 signal_free_time,struct cec_msg * msg)274a8106818SMauro Carvalho Chehab static int rain_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
275a8106818SMauro Carvalho Chehab 				    u32 signal_free_time, struct cec_msg *msg)
276a8106818SMauro Carvalho Chehab {
277a8106818SMauro Carvalho Chehab 	struct rain *rain = cec_get_drvdata(adap);
278a8106818SMauro Carvalho Chehab 	char cmd[2 * CEC_MAX_MSG_SIZE + 16];
279a8106818SMauro Carvalho Chehab 	unsigned int i;
280a8106818SMauro Carvalho Chehab 	int err;
281a8106818SMauro Carvalho Chehab 
282a8106818SMauro Carvalho Chehab 	if (msg->len == 1) {
283a8106818SMauro Carvalho Chehab 		snprintf(cmd, sizeof(cmd), "x%x", cec_msg_destination(msg));
284a8106818SMauro Carvalho Chehab 	} else {
285a8106818SMauro Carvalho Chehab 		char hex[3];
286a8106818SMauro Carvalho Chehab 
287a8106818SMauro Carvalho Chehab 		snprintf(cmd, sizeof(cmd), "x%x %02x ",
288a8106818SMauro Carvalho Chehab 			 cec_msg_destination(msg), msg->msg[1]);
289a8106818SMauro Carvalho Chehab 		for (i = 2; i < msg->len; i++) {
290a8106818SMauro Carvalho Chehab 			snprintf(hex, sizeof(hex), "%02x", msg->msg[i]);
291a8106818SMauro Carvalho Chehab 			strlcat(cmd, hex, sizeof(cmd));
292a8106818SMauro Carvalho Chehab 		}
293a8106818SMauro Carvalho Chehab 	}
294a8106818SMauro Carvalho Chehab 	mutex_lock(&rain->write_lock);
295a8106818SMauro Carvalho Chehab 	err = rain_send(rain, cmd);
296a8106818SMauro Carvalho Chehab 	mutex_unlock(&rain->write_lock);
297a8106818SMauro Carvalho Chehab 	return err;
298a8106818SMauro Carvalho Chehab }
299a8106818SMauro Carvalho Chehab 
300a8106818SMauro Carvalho Chehab static const struct cec_adap_ops rain_cec_adap_ops = {
301a8106818SMauro Carvalho Chehab 	.adap_enable = rain_cec_adap_enable,
302a8106818SMauro Carvalho Chehab 	.adap_log_addr = rain_cec_adap_log_addr,
303a8106818SMauro Carvalho Chehab 	.adap_transmit = rain_cec_adap_transmit,
304a8106818SMauro Carvalho Chehab };
305a8106818SMauro Carvalho Chehab 
rain_connect(struct serio * serio,struct serio_driver * drv)306a8106818SMauro Carvalho Chehab static int rain_connect(struct serio *serio, struct serio_driver *drv)
307a8106818SMauro Carvalho Chehab {
308a8106818SMauro Carvalho Chehab 	u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL;
309a8106818SMauro Carvalho Chehab 	struct rain *rain;
310a8106818SMauro Carvalho Chehab 	int err = -ENOMEM;
311a8106818SMauro Carvalho Chehab 	struct cec_log_addrs log_addrs = {};
312a8106818SMauro Carvalho Chehab 	u16 pa = CEC_PHYS_ADDR_INVALID;
313a8106818SMauro Carvalho Chehab 
314a8106818SMauro Carvalho Chehab 	rain = kzalloc(sizeof(*rain), GFP_KERNEL);
315a8106818SMauro Carvalho Chehab 
316a8106818SMauro Carvalho Chehab 	if (!rain)
317a8106818SMauro Carvalho Chehab 		return -ENOMEM;
318a8106818SMauro Carvalho Chehab 
319a8106818SMauro Carvalho Chehab 	rain->serio = serio;
320a8106818SMauro Carvalho Chehab 	rain->adap = cec_allocate_adapter(&rain_cec_adap_ops, rain,
321a8106818SMauro Carvalho Chehab 					  dev_name(&serio->dev), caps, 1);
322a8106818SMauro Carvalho Chehab 	err = PTR_ERR_OR_ZERO(rain->adap);
323a8106818SMauro Carvalho Chehab 	if (err < 0)
324a8106818SMauro Carvalho Chehab 		goto free_device;
325a8106818SMauro Carvalho Chehab 
326a8106818SMauro Carvalho Chehab 	rain->dev = &serio->dev;
327a8106818SMauro Carvalho Chehab 	serio_set_drvdata(serio, rain);
328a8106818SMauro Carvalho Chehab 	INIT_WORK(&rain->work, rain_irq_work_handler);
329a8106818SMauro Carvalho Chehab 	mutex_init(&rain->write_lock);
330a8106818SMauro Carvalho Chehab 	spin_lock_init(&rain->buf_lock);
331a8106818SMauro Carvalho Chehab 
332a8106818SMauro Carvalho Chehab 	err = serio_open(serio, drv);
333a8106818SMauro Carvalho Chehab 	if (err)
334a8106818SMauro Carvalho Chehab 		goto delete_adap;
335a8106818SMauro Carvalho Chehab 
336a8106818SMauro Carvalho Chehab 	err = rain_setup(rain, serio, &log_addrs, &pa);
337a8106818SMauro Carvalho Chehab 	if (err)
338a8106818SMauro Carvalho Chehab 		goto close_serio;
339a8106818SMauro Carvalho Chehab 
340a8106818SMauro Carvalho Chehab 	err = cec_register_adapter(rain->adap, &serio->dev);
341a8106818SMauro Carvalho Chehab 	if (err < 0)
342a8106818SMauro Carvalho Chehab 		goto close_serio;
343a8106818SMauro Carvalho Chehab 
344a8106818SMauro Carvalho Chehab 	rain->dev = &rain->adap->devnode.dev;
345a8106818SMauro Carvalho Chehab 	return 0;
346a8106818SMauro Carvalho Chehab 
347a8106818SMauro Carvalho Chehab close_serio:
348a8106818SMauro Carvalho Chehab 	serio_close(serio);
349a8106818SMauro Carvalho Chehab delete_adap:
350a8106818SMauro Carvalho Chehab 	cec_delete_adapter(rain->adap);
351a8106818SMauro Carvalho Chehab 	serio_set_drvdata(serio, NULL);
352a8106818SMauro Carvalho Chehab free_device:
353a8106818SMauro Carvalho Chehab 	kfree(rain);
354a8106818SMauro Carvalho Chehab 	return err;
355a8106818SMauro Carvalho Chehab }
356a8106818SMauro Carvalho Chehab 
357a8106818SMauro Carvalho Chehab static const struct serio_device_id rain_serio_ids[] = {
358a8106818SMauro Carvalho Chehab 	{
359a8106818SMauro Carvalho Chehab 		.type	= SERIO_RS232,
360a8106818SMauro Carvalho Chehab 		.proto	= SERIO_RAINSHADOW_CEC,
361a8106818SMauro Carvalho Chehab 		.id	= SERIO_ANY,
362a8106818SMauro Carvalho Chehab 		.extra	= SERIO_ANY,
363a8106818SMauro Carvalho Chehab 	},
364a8106818SMauro Carvalho Chehab 	{ 0 }
365a8106818SMauro Carvalho Chehab };
366a8106818SMauro Carvalho Chehab 
367a8106818SMauro Carvalho Chehab MODULE_DEVICE_TABLE(serio, rain_serio_ids);
368a8106818SMauro Carvalho Chehab 
369a8106818SMauro Carvalho Chehab static struct serio_driver rain_drv = {
370a8106818SMauro Carvalho Chehab 	.driver		= {
371a8106818SMauro Carvalho Chehab 		.name	= "rainshadow-cec",
372a8106818SMauro Carvalho Chehab 	},
373a8106818SMauro Carvalho Chehab 	.description	= "RainShadow Tech HDMI CEC driver",
374a8106818SMauro Carvalho Chehab 	.id_table	= rain_serio_ids,
375a8106818SMauro Carvalho Chehab 	.interrupt	= rain_interrupt,
376a8106818SMauro Carvalho Chehab 	.connect	= rain_connect,
377a8106818SMauro Carvalho Chehab 	.disconnect	= rain_disconnect,
378a8106818SMauro Carvalho Chehab };
379a8106818SMauro Carvalho Chehab 
380a8106818SMauro Carvalho Chehab module_serio_driver(rain_drv);
381