2 * Pulse Eight HDMI CEC driver
4 * Copyright 2016 Hans Verkuil <hverkuil@xs4all.nl
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.
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>
24 #include <media/cec.h>
26 MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
27 MODULE_DESCRIPTION("Pulse Eight HDMI CEC driver");
28 MODULE_LICENSE("GPL");
31 module_param(debug
, int, 0644);
32 MODULE_PARM_DESC(debug
, "debug level (0-1)");
34 enum pulse8_msgcodes
{
37 MSGCODE_TIMEOUT_ERROR
,
42 MSGCODE_RECEIVE_FAILED
,
43 MSGCODE_COMMAND_ACCEPTED
, /* 0x08 */
44 MSGCODE_COMMAND_REJECTED
,
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
,
75 MSGCODE_GET_ADAPTER_TYPE
, /* 0x28 */
76 MSGCODE_SET_ACTIVE_SOURCE
,
78 MSGCODE_FRAME_EOM
= 0x80,
79 MSGCODE_FRAME_ACK
= 0x40,
92 struct cec_adapter
*adap
;
93 struct completion cmd_done
;
94 struct work_struct work
;
95 struct cec_msg rx_msg
;
104 static void pulse8_irq_work_handler(struct work_struct
*work
)
106 struct pulse8
*pulse8
=
107 container_of(work
, struct pulse8
, work
);
109 switch (pulse8
->data
[0] & 0x3f) {
110 case MSGCODE_FRAME_DATA
:
111 cec_received_msg(pulse8
->adap
, &pulse8
->rx_msg
);
113 case MSGCODE_TRANSMIT_SUCCEEDED
:
114 cec_transmit_done(pulse8
->adap
, CEC_TX_STATUS_OK
,
117 case MSGCODE_TRANSMIT_FAILED_LINE
:
118 cec_transmit_done(pulse8
->adap
, CEC_TX_STATUS_ARB_LOST
,
121 case MSGCODE_TRANSMIT_FAILED_ACK
:
122 cec_transmit_done(pulse8
->adap
, CEC_TX_STATUS_NACK
,
125 case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA
:
126 case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE
:
127 cec_transmit_done(pulse8
->adap
, CEC_TX_STATUS_ERROR
,
133 static irqreturn_t
pulse8_interrupt(struct serio
*serio
, unsigned char data
,
136 struct pulse8
*pulse8
= serio_get_drvdata(serio
);
138 if (!pulse8
->started
&& data
!= MSGSTART
)
140 if (data
== MSGESC
) {
141 pulse8
->escape
= true;
144 if (pulse8
->escape
) {
146 pulse8
->escape
= false;
147 } else if (data
== MSGEND
) {
148 struct cec_msg
*msg
= &pulse8
->rx_msg
;
151 dev_info(pulse8
->dev
, "received: %*ph\n",
152 pulse8
->idx
, pulse8
->buf
);
153 pulse8
->data
[0] = pulse8
->buf
[0];
154 switch (pulse8
->buf
[0] & 0x3f) {
155 case MSGCODE_FRAME_START
:
157 msg
->msg
[0] = pulse8
->buf
[1];
159 case MSGCODE_FRAME_DATA
:
160 if (msg
->len
== CEC_MAX_MSG_SIZE
)
162 msg
->msg
[msg
->len
++] = pulse8
->buf
[1];
163 if (pulse8
->buf
[0] & MSGCODE_FRAME_EOM
)
164 schedule_work(&pulse8
->work
);
166 case MSGCODE_TRANSMIT_SUCCEEDED
:
167 case MSGCODE_TRANSMIT_FAILED_LINE
:
168 case MSGCODE_TRANSMIT_FAILED_ACK
:
169 case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA
:
170 case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE
:
171 schedule_work(&pulse8
->work
);
173 case MSGCODE_TIMEOUT_ERROR
:
175 case MSGCODE_COMMAND_ACCEPTED
:
176 case MSGCODE_COMMAND_REJECTED
:
178 if (pulse8
->idx
== 0)
180 memcpy(pulse8
->data
, pulse8
->buf
, pulse8
->idx
);
181 pulse8
->len
= pulse8
->idx
;
182 complete(&pulse8
->cmd_done
);
186 pulse8
->started
= false;
188 } else if (data
== MSGSTART
) {
190 pulse8
->started
= true;
194 if (pulse8
->idx
>= DATA_SIZE
) {
196 "throwing away %d bytes of garbage\n", pulse8
->idx
);
199 pulse8
->buf
[pulse8
->idx
++] = data
;
203 static void pulse8_disconnect(struct serio
*serio
)
205 struct pulse8
*pulse8
= serio_get_drvdata(serio
);
207 cec_unregister_adapter(pulse8
->adap
);
208 dev_info(&serio
->dev
, "disconnected\n");
210 serio_set_drvdata(serio
, NULL
);
214 static int pulse8_send(struct serio
*serio
, const u8
*command
, u8 cmd_len
)
218 err
= serio_write(serio
, MSGSTART
);
221 for (; !err
&& cmd_len
; command
++, cmd_len
--) {
222 if (*command
>= MSGESC
) {
223 err
= serio_write(serio
, MSGESC
);
225 err
= serio_write(serio
, *command
- MSGOFFSET
);
227 err
= serio_write(serio
, *command
);
231 err
= serio_write(serio
, 0xfe);
236 static int pulse8_send_and_wait(struct pulse8
*pulse8
,
237 const u8
*cmd
, u8 cmd_len
, u8 response
, u8 size
)
241 /*dev_info(pulse8->dev, "transmit: %*ph\n", cmd_len, cmd);*/
242 init_completion(&pulse8
->cmd_done
);
244 err
= pulse8_send(pulse8
->serio
, cmd
, cmd_len
);
248 if (!wait_for_completion_timeout(&pulse8
->cmd_done
, HZ
))
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
) {
256 cmd_sc
[0] = MSGCODE_SET_CONTROLLED
;
258 err
= pulse8_send_and_wait(pulse8
, cmd_sc
, 2,
259 MSGCODE_COMMAND_ACCEPTED
, 1);
262 init_completion(&pulse8
->cmd_done
);
264 err
= pulse8_send(pulse8
->serio
, cmd
, cmd_len
);
268 if (!wait_for_completion_timeout(&pulse8
->cmd_done
, HZ
))
272 ((pulse8
->data
[0] & 0x3f) != response
|| pulse8
->len
< size
+ 1)) {
273 dev_info(pulse8
->dev
, "transmit: failed %02x\n",
274 pulse8
->data
[0] & 0x3f);
280 static int pulse8_setup(struct pulse8
*pulse8
, struct serio
*serio
)
282 u8
*data
= pulse8
->data
+ 1;
283 unsigned int count
= 0;
284 unsigned int vers
= 0;
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
;
293 err
= pulse8_send_and_wait(pulse8
, cmd
, 1, cmd
[0], 2);
297 vers
= (data
[0] << 8) | data
[1];
299 dev_info(pulse8
->dev
, "Firmware version %04x\n", vers
);
303 cmd
[0] = MSGCODE_GET_BUILDDATE
;
305 err
= pulse8_send_and_wait(pulse8
, cmd
, 1, cmd
[0], 4);
307 time_t date
= (data
[0] << 24) | (data
[1] << 16) |
308 (data
[2] << 8) | data
[3];
311 time_to_tm(date
, 0, &tm
);
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
);
321 cmd
[0] = MSGCODE_SET_AUTO_ENABLED
;
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");
330 cmd
[0] = MSGCODE_GET_AUTO_ENABLED
;
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
;
339 err
= pulse8_send_and_wait(pulse8
, cmd
, 1,
342 } while (!err
&& data
[0] && count
++ < 5);
350 static int pulse8_cec_adap_enable(struct cec_adapter
*adap
, bool enable
)
352 struct pulse8
*pulse8
= adap
->priv
;
356 cmd
[0] = MSGCODE_SET_CONTROLLED
;
358 err
= pulse8_send_and_wait(pulse8
, cmd
, 2,
359 MSGCODE_COMMAND_ACCEPTED
, 1);
360 return enable
? err
: 0;
363 static int pulse8_cec_adap_log_addr(struct cec_adapter
*adap
, u8 log_addr
)
365 struct pulse8
*pulse8
= adap
->priv
;
370 if (log_addr
!= CEC_LOG_ADDR_INVALID
)
371 mask
= 1 << log_addr
;
372 cmd
[0] = MSGCODE_SET_ACK_MASK
;
374 cmd
[2] = mask
& 0xff;
375 err
= pulse8_send_and_wait(pulse8
, cmd
, 3,
376 MSGCODE_COMMAND_ACCEPTED
, 0);
382 static int pulse8_cec_adap_transmit(struct cec_adapter
*adap
, u8 attempts
,
383 u32 signal_free_time
, struct cec_msg
*msg
)
385 struct pulse8
*pulse8
= adap
->priv
;
390 cmd
[0] = MSGCODE_TRANSMIT_IDLETIME
;
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
);
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];
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
:
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);
422 static int pulse8_received(struct cec_adapter
*adap
, struct cec_msg
*msg
)
427 static const struct cec_adap_ops pulse8_cec_adap_ops
= {
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
,
434 static int pulse8_connect(struct serio
*serio
, struct serio_driver
*drv
)
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
;
441 pulse8
= kzalloc(sizeof(*pulse8
), GFP_KERNEL
);
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
);
453 pulse8
->dev
= &serio
->dev
;
454 serio_set_drvdata(serio
, pulse8
);
455 INIT_WORK(&pulse8
->work
, pulse8_irq_work_handler
);
457 err
= serio_open(serio
, drv
);
461 err
= pulse8_setup(pulse8
, serio
);
465 err
= cec_register_adapter(pulse8
->adap
);
469 pulse8
->dev
= &pulse8
->adap
->devnode
.dev
;
475 cec_delete_adapter(pulse8
->adap
);
476 serio_set_drvdata(serio
, NULL
);
482 static struct serio_device_id pulse8_serio_ids
[] = {
485 .proto
= SERIO_PULSE8_CEC
,
492 MODULE_DEVICE_TABLE(serio
, pulse8_serio_ids
);
494 static struct serio_driver pulse8_drv
= {
496 .name
= "pulse8-cec",
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
,
505 module_serio_driver(pulse8_drv
);