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