]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/staging/media/pulse8-cec/pulse8-cec.c
Merge remote-tracking branches 'asoc/topic/sgtl5000', 'asoc/topic/simple', 'asoc...
[mirror_ubuntu-bionic-kernel.git] / drivers / staging / media / pulse8-cec / pulse8-cec.c
CommitLineData
3dff3106
HV
1/*
2 * Pulse Eight HDMI CEC driver
3 *
4 * Copyright 2016 Hans Verkuil <hverkuil@xs4all.nl
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version of 2 of the License, or (at your
9 * option) any later version. See the file COPYING in the main directory of
10 * this archive for more details.
11 */
12
13#include <linux/completion.h>
14#include <linux/init.h>
15#include <linux/interrupt.h>
16#include <linux/kernel.h>
17#include <linux/module.h>
18#include <linux/workqueue.h>
19#include <linux/serio.h>
20#include <linux/slab.h>
21#include <linux/time.h>
22#include <linux/delay.h>
23
24#include <media/cec.h>
25
26MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
27MODULE_DESCRIPTION("Pulse Eight HDMI CEC driver");
28MODULE_LICENSE("GPL");
29
30static int debug;
31module_param(debug, int, 0644);
32MODULE_PARM_DESC(debug, "debug level (0-1)");
33
34enum pulse8_msgcodes {
35 MSGCODE_NOTHING = 0,
36 MSGCODE_PING,
37 MSGCODE_TIMEOUT_ERROR,
38 MSGCODE_HIGH_ERROR,
39 MSGCODE_LOW_ERROR,
40 MSGCODE_FRAME_START,
41 MSGCODE_FRAME_DATA,
42 MSGCODE_RECEIVE_FAILED,
43 MSGCODE_COMMAND_ACCEPTED, /* 0x08 */
44 MSGCODE_COMMAND_REJECTED,
45 MSGCODE_SET_ACK_MASK,
46 MSGCODE_TRANSMIT,
47 MSGCODE_TRANSMIT_EOM,
48 MSGCODE_TRANSMIT_IDLETIME,
49 MSGCODE_TRANSMIT_ACK_POLARITY,
50 MSGCODE_TRANSMIT_LINE_TIMEOUT,
51 MSGCODE_TRANSMIT_SUCCEEDED, /* 0x10 */
52 MSGCODE_TRANSMIT_FAILED_LINE,
53 MSGCODE_TRANSMIT_FAILED_ACK,
54 MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA,
55 MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE,
56 MSGCODE_FIRMWARE_VERSION,
57 MSGCODE_START_BOOTLOADER,
58 MSGCODE_GET_BUILDDATE,
59 MSGCODE_SET_CONTROLLED, /* 0x18 */
60 MSGCODE_GET_AUTO_ENABLED,
61 MSGCODE_SET_AUTO_ENABLED,
62 MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS,
63 MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS,
64 MSGCODE_GET_LOGICAL_ADDRESS_MASK,
65 MSGCODE_SET_LOGICAL_ADDRESS_MASK,
66 MSGCODE_GET_PHYSICAL_ADDRESS,
67 MSGCODE_SET_PHYSICAL_ADDRESS, /* 0x20 */
68 MSGCODE_GET_DEVICE_TYPE,
69 MSGCODE_SET_DEVICE_TYPE,
70 MSGCODE_GET_HDMI_VERSION,
71 MSGCODE_SET_HDMI_VERSION,
72 MSGCODE_GET_OSD_NAME,
73 MSGCODE_SET_OSD_NAME,
74 MSGCODE_WRITE_EEPROM,
75 MSGCODE_GET_ADAPTER_TYPE, /* 0x28 */
76 MSGCODE_SET_ACTIVE_SOURCE,
77
78 MSGCODE_FRAME_EOM = 0x80,
79 MSGCODE_FRAME_ACK = 0x40,
80};
81
82#define MSGSTART 0xff
83#define MSGEND 0xfe
84#define MSGESC 0xfd
85#define MSGOFFSET 3
86
87#define DATA_SIZE 256
88
89struct pulse8 {
90 struct device *dev;
91 struct serio *serio;
92 struct cec_adapter *adap;
93 struct completion cmd_done;
94 struct work_struct work;
95 struct cec_msg rx_msg;
96 u8 data[DATA_SIZE];
97 unsigned int len;
98 u8 buf[DATA_SIZE];
99 unsigned int idx;
100 bool escape;
101 bool started;
102};
103
9d01315d 104static void pulse8_irq_work_handler(struct work_struct *work)
3dff3106
HV
105{
106 struct pulse8 *pulse8 =
107 container_of(work, struct pulse8, work);
108
109 switch (pulse8->data[0] & 0x3f) {
110 case MSGCODE_FRAME_DATA:
111 cec_received_msg(pulse8->adap, &pulse8->rx_msg);
112 break;
113 case MSGCODE_TRANSMIT_SUCCEEDED:
114 cec_transmit_done(pulse8->adap, CEC_TX_STATUS_OK,
115 0, 0, 0, 0);
116 break;
3dff3106
HV
117 case MSGCODE_TRANSMIT_FAILED_ACK:
118 cec_transmit_done(pulse8->adap, CEC_TX_STATUS_NACK,
119 0, 1, 0, 0);
120 break;
31f58e31 121 case MSGCODE_TRANSMIT_FAILED_LINE:
3dff3106
HV
122 case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
123 case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
124 cec_transmit_done(pulse8->adap, CEC_TX_STATUS_ERROR,
125 0, 0, 0, 1);
126 break;
127 }
128}
129
130static irqreturn_t pulse8_interrupt(struct serio *serio, unsigned char data,
131 unsigned int flags)
132{
133 struct pulse8 *pulse8 = serio_get_drvdata(serio);
134
135 if (!pulse8->started && data != MSGSTART)
136 return IRQ_HANDLED;
137 if (data == MSGESC) {
138 pulse8->escape = true;
139 return IRQ_HANDLED;
140 }
141 if (pulse8->escape) {
142 data += MSGOFFSET;
143 pulse8->escape = false;
144 } else if (data == MSGEND) {
145 struct cec_msg *msg = &pulse8->rx_msg;
146
147 if (debug)
148 dev_info(pulse8->dev, "received: %*ph\n",
149 pulse8->idx, pulse8->buf);
150 pulse8->data[0] = pulse8->buf[0];
151 switch (pulse8->buf[0] & 0x3f) {
152 case MSGCODE_FRAME_START:
153 msg->len = 1;
154 msg->msg[0] = pulse8->buf[1];
155 break;
156 case MSGCODE_FRAME_DATA:
157 if (msg->len == CEC_MAX_MSG_SIZE)
158 break;
159 msg->msg[msg->len++] = pulse8->buf[1];
160 if (pulse8->buf[0] & MSGCODE_FRAME_EOM)
161 schedule_work(&pulse8->work);
162 break;
163 case MSGCODE_TRANSMIT_SUCCEEDED:
164 case MSGCODE_TRANSMIT_FAILED_LINE:
165 case MSGCODE_TRANSMIT_FAILED_ACK:
166 case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
167 case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
168 schedule_work(&pulse8->work);
169 break;
31f58e31
HV
170 case MSGCODE_HIGH_ERROR:
171 case MSGCODE_LOW_ERROR:
172 case MSGCODE_RECEIVE_FAILED:
3dff3106
HV
173 case MSGCODE_TIMEOUT_ERROR:
174 break;
175 case MSGCODE_COMMAND_ACCEPTED:
176 case MSGCODE_COMMAND_REJECTED:
177 default:
178 if (pulse8->idx == 0)
179 break;
180 memcpy(pulse8->data, pulse8->buf, pulse8->idx);
181 pulse8->len = pulse8->idx;
182 complete(&pulse8->cmd_done);
183 break;
184 }
185 pulse8->idx = 0;
186 pulse8->started = false;
187 return IRQ_HANDLED;
188 } else if (data == MSGSTART) {
189 pulse8->idx = 0;
190 pulse8->started = true;
191 return IRQ_HANDLED;
192 }
193
194 if (pulse8->idx >= DATA_SIZE) {
195 dev_dbg(pulse8->dev,
196 "throwing away %d bytes of garbage\n", pulse8->idx);
197 pulse8->idx = 0;
198 }
199 pulse8->buf[pulse8->idx++] = data;
200 return IRQ_HANDLED;
201}
202
203static void pulse8_disconnect(struct serio *serio)
204{
205 struct pulse8 *pulse8 = serio_get_drvdata(serio);
206
207 cec_unregister_adapter(pulse8->adap);
208 dev_info(&serio->dev, "disconnected\n");
209 serio_close(serio);
210 serio_set_drvdata(serio, NULL);
211 kfree(pulse8);
212}
213
214static int pulse8_send(struct serio *serio, const u8 *command, u8 cmd_len)
215{
216 int err = 0;
217
218 err = serio_write(serio, MSGSTART);
219 if (err)
220 return err;
221 for (; !err && cmd_len; command++, cmd_len--) {
222 if (*command >= MSGESC) {
223 err = serio_write(serio, MSGESC);
224 if (!err)
225 err = serio_write(serio, *command - MSGOFFSET);
226 } else {
227 err = serio_write(serio, *command);
228 }
229 }
230 if (!err)
231 err = serio_write(serio, 0xfe);
232
233 return err;
234}
235
236static int pulse8_send_and_wait(struct pulse8 *pulse8,
237 const u8 *cmd, u8 cmd_len, u8 response, u8 size)
238{
239 int err;
240
241 /*dev_info(pulse8->dev, "transmit: %*ph\n", cmd_len, cmd);*/
242 init_completion(&pulse8->cmd_done);
243
244 err = pulse8_send(pulse8->serio, cmd, cmd_len);
245 if (err)
246 return err;
247
248 if (!wait_for_completion_timeout(&pulse8->cmd_done, HZ))
249 return -ETIMEDOUT;
250 if ((pulse8->data[0] & 0x3f) == MSGCODE_COMMAND_REJECTED &&
251 cmd[0] != MSGCODE_SET_CONTROLLED &&
252 cmd[0] != MSGCODE_SET_AUTO_ENABLED &&
253 cmd[0] != MSGCODE_GET_BUILDDATE) {
254 u8 cmd_sc[2];
255
256 cmd_sc[0] = MSGCODE_SET_CONTROLLED;
257 cmd_sc[1] = 1;
258 err = pulse8_send_and_wait(pulse8, cmd_sc, 2,
259 MSGCODE_COMMAND_ACCEPTED, 1);
260 if (err)
261 return err;
262 init_completion(&pulse8->cmd_done);
263
264 err = pulse8_send(pulse8->serio, cmd, cmd_len);
265 if (err)
266 return err;
267
268 if (!wait_for_completion_timeout(&pulse8->cmd_done, HZ))
269 return -ETIMEDOUT;
270 }
271 if (response &&
272 ((pulse8->data[0] & 0x3f) != response || pulse8->len < size + 1)) {
273 dev_info(pulse8->dev, "transmit: failed %02x\n",
274 pulse8->data[0] & 0x3f);
275 return -EIO;
276 }
277 return 0;
278}
279
280static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio)
281{
282 u8 *data = pulse8->data + 1;
283 unsigned int count = 0;
284 unsigned int vers = 0;
285 u8 cmd[2];
286 int err;
287
288 cmd[0] = MSGCODE_PING;
289 err = pulse8_send_and_wait(pulse8, cmd, 1,
290 MSGCODE_COMMAND_ACCEPTED, 0);
291 cmd[0] = MSGCODE_FIRMWARE_VERSION;
292 if (!err)
293 err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2);
294 if (err)
295 return err;
296
297 vers = (data[0] << 8) | data[1];
298
299 dev_info(pulse8->dev, "Firmware version %04x\n", vers);
300 if (vers < 2)
301 return 0;
302
303 cmd[0] = MSGCODE_GET_BUILDDATE;
304 if (!err)
305 err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 4);
306 if (!err) {
307 time_t date = (data[0] << 24) | (data[1] << 16) |
308 (data[2] << 8) | data[3];
309 struct tm tm;
310
311 time_to_tm(date, 0, &tm);
312
313 dev_info(pulse8->dev, "Firmware build date %04ld.%02d.%02d %02d:%02d:%02d\n",
314 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
315 tm.tm_hour, tm.tm_min, tm.tm_sec);
316 }
317
318 do {
319 if (count)
320 msleep(500);
321 cmd[0] = MSGCODE_SET_AUTO_ENABLED;
322 cmd[1] = 0;
323 err = pulse8_send_and_wait(pulse8, cmd, 2,
324 MSGCODE_COMMAND_ACCEPTED, 1);
325 if (err && count == 0) {
326 dev_info(pulse8->dev, "No Auto Enabled supported\n");
327 return 0;
328 }
329
330 cmd[0] = MSGCODE_GET_AUTO_ENABLED;
331 if (!err)
332 err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);
333 if (!err && !data[0]) {
334 cmd[0] = MSGCODE_WRITE_EEPROM;
335 err = pulse8_send_and_wait(pulse8, cmd, 1,
336 MSGCODE_COMMAND_ACCEPTED, 1);
337 cmd[0] = MSGCODE_GET_AUTO_ENABLED;
338 if (!err)
339 err = pulse8_send_and_wait(pulse8, cmd, 1,
340 cmd[0], 1);
341 }
342 } while (!err && data[0] && count++ < 5);
343
344 if (!err && data[0])
345 err = -EIO;
346
347 return err;
348}
349
350static int pulse8_cec_adap_enable(struct cec_adapter *adap, bool enable)
351{
352 struct pulse8 *pulse8 = adap->priv;
353 u8 cmd[16];
354 int err;
355
356 cmd[0] = MSGCODE_SET_CONTROLLED;
357 cmd[1] = enable;
358 err = pulse8_send_and_wait(pulse8, cmd, 2,
359 MSGCODE_COMMAND_ACCEPTED, 1);
360 return enable ? err : 0;
361}
362
363static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
364{
365 struct pulse8 *pulse8 = adap->priv;
366 u16 mask = 0;
367 u8 cmd[3];
368 int err;
369
370 if (log_addr != CEC_LOG_ADDR_INVALID)
371 mask = 1 << log_addr;
372 cmd[0] = MSGCODE_SET_ACK_MASK;
373 cmd[1] = mask >> 8;
374 cmd[2] = mask & 0xff;
375 err = pulse8_send_and_wait(pulse8, cmd, 3,
376 MSGCODE_COMMAND_ACCEPTED, 0);
377 if (mask == 0)
378 return 0;
379 return err;
380}
381
382static int pulse8_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
383 u32 signal_free_time, struct cec_msg *msg)
384{
385 struct pulse8 *pulse8 = adap->priv;
386 u8 cmd[2];
387 unsigned int i;
388 int err;
389
390 cmd[0] = MSGCODE_TRANSMIT_IDLETIME;
1e6e9754 391 cmd[1] = signal_free_time;
3dff3106
HV
392 err = pulse8_send_and_wait(pulse8, cmd, 2,
393 MSGCODE_COMMAND_ACCEPTED, 1);
394 cmd[0] = MSGCODE_TRANSMIT_ACK_POLARITY;
395 cmd[1] = cec_msg_is_broadcast(msg);
396 if (!err)
397 err = pulse8_send_and_wait(pulse8, cmd, 2,
398 MSGCODE_COMMAND_ACCEPTED, 1);
399 cmd[0] = msg->len == 1 ? MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT;
400 cmd[1] = msg->msg[0];
401 if (!err)
402 err = pulse8_send_and_wait(pulse8, cmd, 2,
403 MSGCODE_COMMAND_ACCEPTED, 1);
404 if (!err && msg->len > 1) {
405 cmd[0] = msg->len == 2 ? MSGCODE_TRANSMIT_EOM :
406 MSGCODE_TRANSMIT;
407 cmd[1] = msg->msg[1];
408 err = pulse8_send_and_wait(pulse8, cmd, 2,
409 MSGCODE_COMMAND_ACCEPTED, 1);
410 for (i = 0; !err && i + 2 < msg->len; i++) {
411 cmd[0] = (i + 2 == msg->len - 1) ?
412 MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT;
413 cmd[1] = msg->msg[i + 2];
414 err = pulse8_send_and_wait(pulse8, cmd, 2,
415 MSGCODE_COMMAND_ACCEPTED, 1);
416 }
417 }
418
419 return err;
420}
421
422static int pulse8_received(struct cec_adapter *adap, struct cec_msg *msg)
423{
424 return -ENOMSG;
425}
426
babbf091 427static const struct cec_adap_ops pulse8_cec_adap_ops = {
3dff3106
HV
428 .adap_enable = pulse8_cec_adap_enable,
429 .adap_log_addr = pulse8_cec_adap_log_addr,
430 .adap_transmit = pulse8_cec_adap_transmit,
431 .received = pulse8_received,
432};
433
434static int pulse8_connect(struct serio *serio, struct serio_driver *drv)
435{
436 u32 caps = CEC_CAP_TRANSMIT | CEC_CAP_LOG_ADDRS | CEC_CAP_PHYS_ADDR |
437 CEC_CAP_PASSTHROUGH | CEC_CAP_RC | CEC_CAP_MONITOR_ALL;
438 struct pulse8 *pulse8;
439 int err = -ENOMEM;
440
441 pulse8 = kzalloc(sizeof(*pulse8), GFP_KERNEL);
442
443 if (!pulse8)
444 return -ENOMEM;
445
446 pulse8->serio = serio;
447 pulse8->adap = cec_allocate_adapter(&pulse8_cec_adap_ops, pulse8,
448 "HDMI CEC", caps, 1, &serio->dev);
449 err = PTR_ERR_OR_ZERO(pulse8->adap);
450 if (err < 0)
451 goto free_device;
452
453 pulse8->dev = &serio->dev;
454 serio_set_drvdata(serio, pulse8);
455 INIT_WORK(&pulse8->work, pulse8_irq_work_handler);
456
457 err = serio_open(serio, drv);
458 if (err)
459 goto delete_adap;
460
461 err = pulse8_setup(pulse8, serio);
462 if (err)
463 goto close_serio;
464
465 err = cec_register_adapter(pulse8->adap);
466 if (err < 0)
467 goto close_serio;
468
469 pulse8->dev = &pulse8->adap->devnode.dev;
470 return 0;
471
472close_serio:
473 serio_close(serio);
474delete_adap:
475 cec_delete_adapter(pulse8->adap);
476 serio_set_drvdata(serio, NULL);
477free_device:
478 kfree(pulse8);
479 return err;
480}
481
482static struct serio_device_id pulse8_serio_ids[] = {
483 {
484 .type = SERIO_RS232,
485 .proto = SERIO_PULSE8_CEC,
486 .id = SERIO_ANY,
487 .extra = SERIO_ANY,
488 },
489 { 0 }
490};
491
492MODULE_DEVICE_TABLE(serio, pulse8_serio_ids);
493
494static struct serio_driver pulse8_drv = {
495 .driver = {
496 .name = "pulse8-cec",
497 },
498 .description = "Pulse Eight HDMI CEC driver",
499 .id_table = pulse8_serio_ids,
500 .interrupt = pulse8_interrupt,
501 .connect = pulse8_connect,
502 .disconnect = pulse8_disconnect,
503};
504
505module_serio_driver(pulse8_drv);