1a8106818SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0-or-later
2a8106818SMauro Carvalho Chehab /*
3a8106818SMauro Carvalho Chehab * Pulse Eight 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 * - Devices with firmware version < 2 do not store their configuration in
12a8106818SMauro Carvalho Chehab * EEPROM.
13a8106818SMauro Carvalho Chehab *
14a8106818SMauro Carvalho Chehab * - In autonomous mode, only messages from a TV will be acknowledged, even
15a8106818SMauro Carvalho Chehab * polling messages. Upon receiving a message from a TV, the dongle will
16a8106818SMauro Carvalho Chehab * respond to messages from any logical address.
17a8106818SMauro Carvalho Chehab *
18a8106818SMauro Carvalho Chehab * - In autonomous mode, the dongle will by default reply Feature Abort
19a8106818SMauro Carvalho Chehab * [Unrecognized Opcode] when it receives Give Device Vendor ID. It will
20a8106818SMauro Carvalho Chehab * however observe vendor ID's reported by other devices and possibly
21a8106818SMauro Carvalho Chehab * alter this behavior. When TV's (and TV's only) report that their vendor ID
22a8106818SMauro Carvalho Chehab * is LG (0x00e091), the dongle will itself reply that it has the same vendor
23a8106818SMauro Carvalho Chehab * ID, and it will respond to at least one vendor specific command.
24a8106818SMauro Carvalho Chehab *
25a8106818SMauro Carvalho Chehab * - In autonomous mode, the dongle is known to attempt wakeup if it receives
26a8106818SMauro Carvalho Chehab * <User Control Pressed> ["Power On"], ["Power] or ["Power Toggle"], or if it
27a8106818SMauro Carvalho Chehab * receives <Set Stream Path> with its own physical address. It also does this
28a8106818SMauro Carvalho Chehab * if it receives <Vendor Specific Command> [0x03 0x00] from an LG TV.
29a8106818SMauro Carvalho Chehab */
30a8106818SMauro Carvalho Chehab
31a8106818SMauro Carvalho Chehab #include <linux/completion.h>
32a8106818SMauro Carvalho Chehab #include <linux/init.h>
33a8106818SMauro Carvalho Chehab #include <linux/interrupt.h>
34a8106818SMauro Carvalho Chehab #include <linux/kernel.h>
35a8106818SMauro Carvalho Chehab #include <linux/module.h>
36a8106818SMauro Carvalho Chehab #include <linux/workqueue.h>
37a8106818SMauro Carvalho Chehab #include <linux/serio.h>
38a8106818SMauro Carvalho Chehab #include <linux/slab.h>
39a8106818SMauro Carvalho Chehab #include <linux/time.h>
40a8106818SMauro Carvalho Chehab #include <linux/delay.h>
41a8106818SMauro Carvalho Chehab
42a8106818SMauro Carvalho Chehab #include <media/cec.h>
43a8106818SMauro Carvalho Chehab
44a8106818SMauro Carvalho Chehab MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
45a8106818SMauro Carvalho Chehab MODULE_DESCRIPTION("Pulse Eight HDMI CEC driver");
46a8106818SMauro Carvalho Chehab MODULE_LICENSE("GPL");
47a8106818SMauro Carvalho Chehab
48a8106818SMauro Carvalho Chehab static int debug;
49a8106818SMauro Carvalho Chehab static int persistent_config;
50a8106818SMauro Carvalho Chehab module_param(debug, int, 0644);
51a8106818SMauro Carvalho Chehab module_param(persistent_config, int, 0644);
52a8106818SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "debug level (0-2)");
53a8106818SMauro Carvalho Chehab MODULE_PARM_DESC(persistent_config, "read config from persistent memory (0-1)");
54a8106818SMauro Carvalho Chehab
55a8106818SMauro Carvalho Chehab enum pulse8_msgcodes {
56a8106818SMauro Carvalho Chehab MSGCODE_NOTHING = 0,
57a8106818SMauro Carvalho Chehab MSGCODE_PING,
58a8106818SMauro Carvalho Chehab MSGCODE_TIMEOUT_ERROR,
59a8106818SMauro Carvalho Chehab MSGCODE_HIGH_ERROR,
60a8106818SMauro Carvalho Chehab MSGCODE_LOW_ERROR,
61a8106818SMauro Carvalho Chehab MSGCODE_FRAME_START,
62a8106818SMauro Carvalho Chehab MSGCODE_FRAME_DATA,
63a8106818SMauro Carvalho Chehab MSGCODE_RECEIVE_FAILED,
64a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, /* 0x08 */
65a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_REJECTED,
66a8106818SMauro Carvalho Chehab MSGCODE_SET_ACK_MASK,
67a8106818SMauro Carvalho Chehab MSGCODE_TRANSMIT,
68a8106818SMauro Carvalho Chehab MSGCODE_TRANSMIT_EOM,
69a8106818SMauro Carvalho Chehab MSGCODE_TRANSMIT_IDLETIME,
70a8106818SMauro Carvalho Chehab MSGCODE_TRANSMIT_ACK_POLARITY,
71a8106818SMauro Carvalho Chehab MSGCODE_TRANSMIT_LINE_TIMEOUT,
72a8106818SMauro Carvalho Chehab MSGCODE_TRANSMIT_SUCCEEDED, /* 0x10 */
73a8106818SMauro Carvalho Chehab MSGCODE_TRANSMIT_FAILED_LINE,
74a8106818SMauro Carvalho Chehab MSGCODE_TRANSMIT_FAILED_ACK,
75a8106818SMauro Carvalho Chehab MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA,
76a8106818SMauro Carvalho Chehab MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE,
77a8106818SMauro Carvalho Chehab MSGCODE_FIRMWARE_VERSION,
78a8106818SMauro Carvalho Chehab MSGCODE_START_BOOTLOADER,
79a8106818SMauro Carvalho Chehab MSGCODE_GET_BUILDDATE,
80a8106818SMauro Carvalho Chehab MSGCODE_SET_CONTROLLED, /* 0x18 */
81a8106818SMauro Carvalho Chehab MSGCODE_GET_AUTO_ENABLED,
82a8106818SMauro Carvalho Chehab MSGCODE_SET_AUTO_ENABLED,
83a8106818SMauro Carvalho Chehab MSGCODE_GET_DEFAULT_LOGICAL_ADDRESS,
84a8106818SMauro Carvalho Chehab MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS,
85a8106818SMauro Carvalho Chehab MSGCODE_GET_LOGICAL_ADDRESS_MASK,
86a8106818SMauro Carvalho Chehab MSGCODE_SET_LOGICAL_ADDRESS_MASK,
87a8106818SMauro Carvalho Chehab MSGCODE_GET_PHYSICAL_ADDRESS,
88a8106818SMauro Carvalho Chehab MSGCODE_SET_PHYSICAL_ADDRESS, /* 0x20 */
89a8106818SMauro Carvalho Chehab MSGCODE_GET_DEVICE_TYPE,
90a8106818SMauro Carvalho Chehab MSGCODE_SET_DEVICE_TYPE,
9145ba1c0bSHans Verkuil MSGCODE_GET_HDMI_VERSION, /* Removed in FW >= 10 */
92a8106818SMauro Carvalho Chehab MSGCODE_SET_HDMI_VERSION,
93a8106818SMauro Carvalho Chehab MSGCODE_GET_OSD_NAME,
94a8106818SMauro Carvalho Chehab MSGCODE_SET_OSD_NAME,
95a8106818SMauro Carvalho Chehab MSGCODE_WRITE_EEPROM,
96a8106818SMauro Carvalho Chehab MSGCODE_GET_ADAPTER_TYPE, /* 0x28 */
97a8106818SMauro Carvalho Chehab MSGCODE_SET_ACTIVE_SOURCE,
9845ba1c0bSHans Verkuil MSGCODE_GET_AUTO_POWER_ON, /* New for FW >= 10 */
9945ba1c0bSHans Verkuil MSGCODE_SET_AUTO_POWER_ON,
100a8106818SMauro Carvalho Chehab
101a8106818SMauro Carvalho Chehab MSGCODE_FRAME_EOM = 0x80,
102a8106818SMauro Carvalho Chehab MSGCODE_FRAME_ACK = 0x40,
103a8106818SMauro Carvalho Chehab };
104a8106818SMauro Carvalho Chehab
105a8106818SMauro Carvalho Chehab static const char * const pulse8_msgnames[] = {
106a8106818SMauro Carvalho Chehab "NOTHING",
107a8106818SMauro Carvalho Chehab "PING",
108a8106818SMauro Carvalho Chehab "TIMEOUT_ERROR",
109a8106818SMauro Carvalho Chehab "HIGH_ERROR",
110a8106818SMauro Carvalho Chehab "LOW_ERROR",
111a8106818SMauro Carvalho Chehab "FRAME_START",
112a8106818SMauro Carvalho Chehab "FRAME_DATA",
113a8106818SMauro Carvalho Chehab "RECEIVE_FAILED",
114a8106818SMauro Carvalho Chehab "COMMAND_ACCEPTED",
115a8106818SMauro Carvalho Chehab "COMMAND_REJECTED",
116a8106818SMauro Carvalho Chehab "SET_ACK_MASK",
117a8106818SMauro Carvalho Chehab "TRANSMIT",
118a8106818SMauro Carvalho Chehab "TRANSMIT_EOM",
119a8106818SMauro Carvalho Chehab "TRANSMIT_IDLETIME",
120a8106818SMauro Carvalho Chehab "TRANSMIT_ACK_POLARITY",
121a8106818SMauro Carvalho Chehab "TRANSMIT_LINE_TIMEOUT",
122a8106818SMauro Carvalho Chehab "TRANSMIT_SUCCEEDED",
123a8106818SMauro Carvalho Chehab "TRANSMIT_FAILED_LINE",
124a8106818SMauro Carvalho Chehab "TRANSMIT_FAILED_ACK",
125a8106818SMauro Carvalho Chehab "TRANSMIT_FAILED_TIMEOUT_DATA",
126a8106818SMauro Carvalho Chehab "TRANSMIT_FAILED_TIMEOUT_LINE",
127a8106818SMauro Carvalho Chehab "FIRMWARE_VERSION",
128a8106818SMauro Carvalho Chehab "START_BOOTLOADER",
129a8106818SMauro Carvalho Chehab "GET_BUILDDATE",
130a8106818SMauro Carvalho Chehab "SET_CONTROLLED",
131a8106818SMauro Carvalho Chehab "GET_AUTO_ENABLED",
132a8106818SMauro Carvalho Chehab "SET_AUTO_ENABLED",
133a8106818SMauro Carvalho Chehab "GET_DEFAULT_LOGICAL_ADDRESS",
134a8106818SMauro Carvalho Chehab "SET_DEFAULT_LOGICAL_ADDRESS",
135a8106818SMauro Carvalho Chehab "GET_LOGICAL_ADDRESS_MASK",
136a8106818SMauro Carvalho Chehab "SET_LOGICAL_ADDRESS_MASK",
137a8106818SMauro Carvalho Chehab "GET_PHYSICAL_ADDRESS",
138a8106818SMauro Carvalho Chehab "SET_PHYSICAL_ADDRESS",
139a8106818SMauro Carvalho Chehab "GET_DEVICE_TYPE",
140a8106818SMauro Carvalho Chehab "SET_DEVICE_TYPE",
141a8106818SMauro Carvalho Chehab "GET_HDMI_VERSION",
142a8106818SMauro Carvalho Chehab "SET_HDMI_VERSION",
143a8106818SMauro Carvalho Chehab "GET_OSD_NAME",
144a8106818SMauro Carvalho Chehab "SET_OSD_NAME",
145a8106818SMauro Carvalho Chehab "WRITE_EEPROM",
146a8106818SMauro Carvalho Chehab "GET_ADAPTER_TYPE",
147a8106818SMauro Carvalho Chehab "SET_ACTIVE_SOURCE",
14845ba1c0bSHans Verkuil "GET_AUTO_POWER_ON",
14945ba1c0bSHans Verkuil "SET_AUTO_POWER_ON",
150a8106818SMauro Carvalho Chehab };
151a8106818SMauro Carvalho Chehab
pulse8_msgname(u8 cmd)152a8106818SMauro Carvalho Chehab static const char *pulse8_msgname(u8 cmd)
153a8106818SMauro Carvalho Chehab {
154a8106818SMauro Carvalho Chehab static char unknown_msg[5];
155a8106818SMauro Carvalho Chehab
156a8106818SMauro Carvalho Chehab if ((cmd & 0x3f) < ARRAY_SIZE(pulse8_msgnames))
157a8106818SMauro Carvalho Chehab return pulse8_msgnames[cmd & 0x3f];
158a8106818SMauro Carvalho Chehab snprintf(unknown_msg, sizeof(unknown_msg), "0x%02x", cmd);
159a8106818SMauro Carvalho Chehab return unknown_msg;
160a8106818SMauro Carvalho Chehab }
161a8106818SMauro Carvalho Chehab
162a8106818SMauro Carvalho Chehab #define MSGSTART 0xff
163a8106818SMauro Carvalho Chehab #define MSGEND 0xfe
164a8106818SMauro Carvalho Chehab #define MSGESC 0xfd
165a8106818SMauro Carvalho Chehab #define MSGOFFSET 3
166a8106818SMauro Carvalho Chehab
167a8106818SMauro Carvalho Chehab #define DATA_SIZE 256
168a8106818SMauro Carvalho Chehab
169a8106818SMauro Carvalho Chehab #define PING_PERIOD (15 * HZ)
170a8106818SMauro Carvalho Chehab
171a8106818SMauro Carvalho Chehab #define NUM_MSGS 8
172a8106818SMauro Carvalho Chehab
173a8106818SMauro Carvalho Chehab struct pulse8 {
174a8106818SMauro Carvalho Chehab struct device *dev;
175a8106818SMauro Carvalho Chehab struct serio *serio;
176a8106818SMauro Carvalho Chehab struct cec_adapter *adap;
177a8106818SMauro Carvalho Chehab unsigned int vers;
178a8106818SMauro Carvalho Chehab
179a8106818SMauro Carvalho Chehab struct delayed_work ping_eeprom_work;
180a8106818SMauro Carvalho Chehab
181a8106818SMauro Carvalho Chehab struct work_struct irq_work;
182a8106818SMauro Carvalho Chehab struct cec_msg rx_msg[NUM_MSGS];
183a8106818SMauro Carvalho Chehab unsigned int rx_msg_cur_idx, rx_msg_num;
184a8106818SMauro Carvalho Chehab /* protect rx_msg_cur_idx and rx_msg_num */
185a8106818SMauro Carvalho Chehab spinlock_t msg_lock;
186a8106818SMauro Carvalho Chehab u8 new_rx_msg[CEC_MAX_MSG_SIZE];
187a8106818SMauro Carvalho Chehab u8 new_rx_msg_len;
188a8106818SMauro Carvalho Chehab
189a8106818SMauro Carvalho Chehab struct work_struct tx_work;
190a8106818SMauro Carvalho Chehab u32 tx_done_status;
191a8106818SMauro Carvalho Chehab u32 tx_signal_free_time;
192a8106818SMauro Carvalho Chehab struct cec_msg tx_msg;
193a8106818SMauro Carvalho Chehab bool tx_msg_is_bcast;
194a8106818SMauro Carvalho Chehab
195a8106818SMauro Carvalho Chehab struct completion cmd_done;
196a8106818SMauro Carvalho Chehab u8 data[DATA_SIZE];
197a8106818SMauro Carvalho Chehab unsigned int len;
198a8106818SMauro Carvalho Chehab u8 buf[DATA_SIZE];
199a8106818SMauro Carvalho Chehab unsigned int idx;
200a8106818SMauro Carvalho Chehab bool escape;
201a8106818SMauro Carvalho Chehab bool started;
202a8106818SMauro Carvalho Chehab
203a8106818SMauro Carvalho Chehab /* locks access to the adapter */
204a8106818SMauro Carvalho Chehab struct mutex lock;
205a8106818SMauro Carvalho Chehab bool config_pending;
206a8106818SMauro Carvalho Chehab bool restoring_config;
207a8106818SMauro Carvalho Chehab bool autonomous;
208a8106818SMauro Carvalho Chehab };
209a8106818SMauro Carvalho Chehab
pulse8_send(struct serio * serio,const u8 * command,u8 cmd_len)210a8106818SMauro Carvalho Chehab static int pulse8_send(struct serio *serio, const u8 *command, u8 cmd_len)
211a8106818SMauro Carvalho Chehab {
212a8106818SMauro Carvalho Chehab int err = 0;
213a8106818SMauro Carvalho Chehab
214a8106818SMauro Carvalho Chehab err = serio_write(serio, MSGSTART);
215a8106818SMauro Carvalho Chehab if (err)
216a8106818SMauro Carvalho Chehab return err;
217a8106818SMauro Carvalho Chehab for (; !err && cmd_len; command++, cmd_len--) {
218a8106818SMauro Carvalho Chehab if (*command >= MSGESC) {
219a8106818SMauro Carvalho Chehab err = serio_write(serio, MSGESC);
220a8106818SMauro Carvalho Chehab if (!err)
221a8106818SMauro Carvalho Chehab err = serio_write(serio, *command - MSGOFFSET);
222a8106818SMauro Carvalho Chehab } else {
223a8106818SMauro Carvalho Chehab err = serio_write(serio, *command);
224a8106818SMauro Carvalho Chehab }
225a8106818SMauro Carvalho Chehab }
226a8106818SMauro Carvalho Chehab if (!err)
227a8106818SMauro Carvalho Chehab err = serio_write(serio, MSGEND);
228a8106818SMauro Carvalho Chehab
229a8106818SMauro Carvalho Chehab return err;
230a8106818SMauro Carvalho Chehab }
231a8106818SMauro Carvalho Chehab
pulse8_send_and_wait_once(struct pulse8 * pulse8,const u8 * cmd,u8 cmd_len,u8 response,u8 size)232a8106818SMauro Carvalho Chehab static int pulse8_send_and_wait_once(struct pulse8 *pulse8,
233a8106818SMauro Carvalho Chehab const u8 *cmd, u8 cmd_len,
234a8106818SMauro Carvalho Chehab u8 response, u8 size)
235a8106818SMauro Carvalho Chehab {
236a8106818SMauro Carvalho Chehab int err;
237a8106818SMauro Carvalho Chehab
238a8106818SMauro Carvalho Chehab if (debug > 1)
239a8106818SMauro Carvalho Chehab dev_info(pulse8->dev, "transmit %s: %*ph\n",
240a8106818SMauro Carvalho Chehab pulse8_msgname(cmd[0]), cmd_len, cmd);
241a8106818SMauro Carvalho Chehab init_completion(&pulse8->cmd_done);
242a8106818SMauro Carvalho Chehab
243a8106818SMauro Carvalho Chehab err = pulse8_send(pulse8->serio, cmd, cmd_len);
244a8106818SMauro Carvalho Chehab if (err)
245a8106818SMauro Carvalho Chehab return err;
246a8106818SMauro Carvalho Chehab
247a8106818SMauro Carvalho Chehab if (!wait_for_completion_timeout(&pulse8->cmd_done, HZ))
248a8106818SMauro Carvalho Chehab return -ETIMEDOUT;
249a8106818SMauro Carvalho Chehab if ((pulse8->data[0] & 0x3f) == MSGCODE_COMMAND_REJECTED &&
250a8106818SMauro Carvalho Chehab cmd[0] != MSGCODE_SET_CONTROLLED &&
251a8106818SMauro Carvalho Chehab cmd[0] != MSGCODE_SET_AUTO_ENABLED &&
252a8106818SMauro Carvalho Chehab cmd[0] != MSGCODE_GET_BUILDDATE)
253a8106818SMauro Carvalho Chehab return -ENOTTY;
254a8106818SMauro Carvalho Chehab if (response &&
255a8106818SMauro Carvalho Chehab ((pulse8->data[0] & 0x3f) != response || pulse8->len < size + 1)) {
256a8106818SMauro Carvalho Chehab dev_info(pulse8->dev, "transmit %s failed with %s\n",
257a8106818SMauro Carvalho Chehab pulse8_msgname(cmd[0]),
258a8106818SMauro Carvalho Chehab pulse8_msgname(pulse8->data[0]));
259a8106818SMauro Carvalho Chehab return -EIO;
260a8106818SMauro Carvalho Chehab }
261a8106818SMauro Carvalho Chehab return 0;
262a8106818SMauro Carvalho Chehab }
263a8106818SMauro Carvalho Chehab
pulse8_send_and_wait(struct pulse8 * pulse8,const u8 * cmd,u8 cmd_len,u8 response,u8 size)264a8106818SMauro Carvalho Chehab static int pulse8_send_and_wait(struct pulse8 *pulse8,
265a8106818SMauro Carvalho Chehab const u8 *cmd, u8 cmd_len, u8 response, u8 size)
266a8106818SMauro Carvalho Chehab {
267a8106818SMauro Carvalho Chehab u8 cmd_sc[2];
268a8106818SMauro Carvalho Chehab int err;
269a8106818SMauro Carvalho Chehab
270a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len, response, size);
271a8106818SMauro Carvalho Chehab if (err != -ENOTTY)
272a8106818SMauro Carvalho Chehab return err;
273a8106818SMauro Carvalho Chehab
274a8106818SMauro Carvalho Chehab cmd_sc[0] = MSGCODE_SET_CONTROLLED;
275a8106818SMauro Carvalho Chehab cmd_sc[1] = 1;
276a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait_once(pulse8, cmd_sc, 2,
277a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 1);
278a8106818SMauro Carvalho Chehab if (!err)
279a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait_once(pulse8, cmd, cmd_len,
280a8106818SMauro Carvalho Chehab response, size);
281a8106818SMauro Carvalho Chehab return err == -ENOTTY ? -EIO : err;
282a8106818SMauro Carvalho Chehab }
283a8106818SMauro Carvalho Chehab
pulse8_tx_work_handler(struct work_struct * work)284a8106818SMauro Carvalho Chehab static void pulse8_tx_work_handler(struct work_struct *work)
285a8106818SMauro Carvalho Chehab {
286a8106818SMauro Carvalho Chehab struct pulse8 *pulse8 = container_of(work, struct pulse8, tx_work);
287a8106818SMauro Carvalho Chehab struct cec_msg *msg = &pulse8->tx_msg;
288a8106818SMauro Carvalho Chehab unsigned int i;
289a8106818SMauro Carvalho Chehab u8 cmd[2];
290a8106818SMauro Carvalho Chehab int err;
291a8106818SMauro Carvalho Chehab
292a8106818SMauro Carvalho Chehab if (msg->len == 0)
293a8106818SMauro Carvalho Chehab return;
294a8106818SMauro Carvalho Chehab
295a8106818SMauro Carvalho Chehab mutex_lock(&pulse8->lock);
296a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_TRANSMIT_IDLETIME;
297a8106818SMauro Carvalho Chehab cmd[1] = pulse8->tx_signal_free_time;
298a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 2,
299a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 1);
300a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_TRANSMIT_ACK_POLARITY;
301a8106818SMauro Carvalho Chehab cmd[1] = cec_msg_is_broadcast(msg);
302a8106818SMauro Carvalho Chehab pulse8->tx_msg_is_bcast = cec_msg_is_broadcast(msg);
303a8106818SMauro Carvalho Chehab if (!err)
304a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 2,
305a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 1);
306a8106818SMauro Carvalho Chehab cmd[0] = msg->len == 1 ? MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT;
307a8106818SMauro Carvalho Chehab cmd[1] = msg->msg[0];
308a8106818SMauro Carvalho Chehab if (!err)
309a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 2,
310a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 1);
311a8106818SMauro Carvalho Chehab if (!err && msg->len > 1) {
312a8106818SMauro Carvalho Chehab for (i = 1; !err && i < msg->len; i++) {
313a8106818SMauro Carvalho Chehab cmd[0] = ((i == msg->len - 1)) ?
314a8106818SMauro Carvalho Chehab MSGCODE_TRANSMIT_EOM : MSGCODE_TRANSMIT;
315a8106818SMauro Carvalho Chehab cmd[1] = msg->msg[i];
316a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 2,
317a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 1);
318a8106818SMauro Carvalho Chehab }
319a8106818SMauro Carvalho Chehab }
320a8106818SMauro Carvalho Chehab if (err && debug)
321a8106818SMauro Carvalho Chehab dev_info(pulse8->dev, "%s(0x%02x) failed with error %d for msg %*ph\n",
322a8106818SMauro Carvalho Chehab pulse8_msgname(cmd[0]), cmd[1],
323a8106818SMauro Carvalho Chehab err, msg->len, msg->msg);
324a8106818SMauro Carvalho Chehab msg->len = 0;
325a8106818SMauro Carvalho Chehab mutex_unlock(&pulse8->lock);
326a8106818SMauro Carvalho Chehab if (err)
327a8106818SMauro Carvalho Chehab cec_transmit_attempt_done(pulse8->adap, CEC_TX_STATUS_ERROR);
328a8106818SMauro Carvalho Chehab }
329a8106818SMauro Carvalho Chehab
pulse8_irq_work_handler(struct work_struct * work)330a8106818SMauro Carvalho Chehab static void pulse8_irq_work_handler(struct work_struct *work)
331a8106818SMauro Carvalho Chehab {
332a8106818SMauro Carvalho Chehab struct pulse8 *pulse8 =
333a8106818SMauro Carvalho Chehab container_of(work, struct pulse8, irq_work);
334a8106818SMauro Carvalho Chehab unsigned long flags;
335a8106818SMauro Carvalho Chehab u32 status;
336a8106818SMauro Carvalho Chehab
337a8106818SMauro Carvalho Chehab spin_lock_irqsave(&pulse8->msg_lock, flags);
338a8106818SMauro Carvalho Chehab while (pulse8->rx_msg_num) {
339a8106818SMauro Carvalho Chehab spin_unlock_irqrestore(&pulse8->msg_lock, flags);
340a8106818SMauro Carvalho Chehab if (debug)
341a8106818SMauro Carvalho Chehab dev_info(pulse8->dev, "adap received %*ph\n",
342a8106818SMauro Carvalho Chehab pulse8->rx_msg[pulse8->rx_msg_cur_idx].len,
343a8106818SMauro Carvalho Chehab pulse8->rx_msg[pulse8->rx_msg_cur_idx].msg);
344a8106818SMauro Carvalho Chehab cec_received_msg(pulse8->adap,
345a8106818SMauro Carvalho Chehab &pulse8->rx_msg[pulse8->rx_msg_cur_idx]);
346a8106818SMauro Carvalho Chehab spin_lock_irqsave(&pulse8->msg_lock, flags);
347a8106818SMauro Carvalho Chehab if (pulse8->rx_msg_num)
348a8106818SMauro Carvalho Chehab pulse8->rx_msg_num--;
349a8106818SMauro Carvalho Chehab pulse8->rx_msg_cur_idx =
350a8106818SMauro Carvalho Chehab (pulse8->rx_msg_cur_idx + 1) % NUM_MSGS;
351a8106818SMauro Carvalho Chehab }
352a8106818SMauro Carvalho Chehab spin_unlock_irqrestore(&pulse8->msg_lock, flags);
353a8106818SMauro Carvalho Chehab
354a8106818SMauro Carvalho Chehab mutex_lock(&pulse8->lock);
355a8106818SMauro Carvalho Chehab status = pulse8->tx_done_status;
356a8106818SMauro Carvalho Chehab pulse8->tx_done_status = 0;
357a8106818SMauro Carvalho Chehab mutex_unlock(&pulse8->lock);
358a8106818SMauro Carvalho Chehab if (status)
359a8106818SMauro Carvalho Chehab cec_transmit_attempt_done(pulse8->adap, status);
360a8106818SMauro Carvalho Chehab }
361a8106818SMauro Carvalho Chehab
pulse8_interrupt(struct serio * serio,unsigned char data,unsigned int flags)362a8106818SMauro Carvalho Chehab static irqreturn_t pulse8_interrupt(struct serio *serio, unsigned char data,
363a8106818SMauro Carvalho Chehab unsigned int flags)
364a8106818SMauro Carvalho Chehab {
365a8106818SMauro Carvalho Chehab struct pulse8 *pulse8 = serio_get_drvdata(serio);
366a8106818SMauro Carvalho Chehab unsigned long irq_flags;
367a8106818SMauro Carvalho Chehab unsigned int idx;
368a8106818SMauro Carvalho Chehab
369a8106818SMauro Carvalho Chehab if (!pulse8->started && data != MSGSTART)
370a8106818SMauro Carvalho Chehab return IRQ_HANDLED;
371a8106818SMauro Carvalho Chehab if (data == MSGESC) {
372a8106818SMauro Carvalho Chehab pulse8->escape = true;
373a8106818SMauro Carvalho Chehab return IRQ_HANDLED;
374a8106818SMauro Carvalho Chehab }
375a8106818SMauro Carvalho Chehab if (pulse8->escape) {
376a8106818SMauro Carvalho Chehab data += MSGOFFSET;
377a8106818SMauro Carvalho Chehab pulse8->escape = false;
378a8106818SMauro Carvalho Chehab } else if (data == MSGEND) {
379a8106818SMauro Carvalho Chehab u8 msgcode = pulse8->buf[0];
380a8106818SMauro Carvalho Chehab
381a8106818SMauro Carvalho Chehab if (debug > 1)
382a8106818SMauro Carvalho Chehab dev_info(pulse8->dev, "received %s: %*ph\n",
383a8106818SMauro Carvalho Chehab pulse8_msgname(msgcode),
384a8106818SMauro Carvalho Chehab pulse8->idx, pulse8->buf);
385a8106818SMauro Carvalho Chehab switch (msgcode & 0x3f) {
386a8106818SMauro Carvalho Chehab case MSGCODE_FRAME_START:
387a8106818SMauro Carvalho Chehab /*
388a8106818SMauro Carvalho Chehab * Test if we are receiving a new msg when a previous
389a8106818SMauro Carvalho Chehab * message is still pending.
390a8106818SMauro Carvalho Chehab */
391a8106818SMauro Carvalho Chehab if (!(msgcode & MSGCODE_FRAME_EOM)) {
392a8106818SMauro Carvalho Chehab pulse8->new_rx_msg_len = 1;
393a8106818SMauro Carvalho Chehab pulse8->new_rx_msg[0] = pulse8->buf[1];
394a8106818SMauro Carvalho Chehab break;
395a8106818SMauro Carvalho Chehab }
3961771e9fbSGustavo A. R. Silva fallthrough;
397a8106818SMauro Carvalho Chehab case MSGCODE_FRAME_DATA:
398a8106818SMauro Carvalho Chehab if (pulse8->new_rx_msg_len < CEC_MAX_MSG_SIZE)
399a8106818SMauro Carvalho Chehab pulse8->new_rx_msg[pulse8->new_rx_msg_len++] =
400a8106818SMauro Carvalho Chehab pulse8->buf[1];
401a8106818SMauro Carvalho Chehab if (!(msgcode & MSGCODE_FRAME_EOM))
402a8106818SMauro Carvalho Chehab break;
403a8106818SMauro Carvalho Chehab
404a8106818SMauro Carvalho Chehab spin_lock_irqsave(&pulse8->msg_lock, irq_flags);
405a8106818SMauro Carvalho Chehab idx = (pulse8->rx_msg_cur_idx + pulse8->rx_msg_num) %
406a8106818SMauro Carvalho Chehab NUM_MSGS;
407a8106818SMauro Carvalho Chehab if (pulse8->rx_msg_num == NUM_MSGS) {
408a8106818SMauro Carvalho Chehab dev_warn(pulse8->dev,
409a8106818SMauro Carvalho Chehab "message queue is full, dropping %*ph\n",
410a8106818SMauro Carvalho Chehab pulse8->new_rx_msg_len,
411a8106818SMauro Carvalho Chehab pulse8->new_rx_msg);
412a8106818SMauro Carvalho Chehab spin_unlock_irqrestore(&pulse8->msg_lock,
413a8106818SMauro Carvalho Chehab irq_flags);
414a8106818SMauro Carvalho Chehab pulse8->new_rx_msg_len = 0;
415a8106818SMauro Carvalho Chehab break;
416a8106818SMauro Carvalho Chehab }
417a8106818SMauro Carvalho Chehab pulse8->rx_msg_num++;
418a8106818SMauro Carvalho Chehab memcpy(pulse8->rx_msg[idx].msg, pulse8->new_rx_msg,
419a8106818SMauro Carvalho Chehab pulse8->new_rx_msg_len);
420a8106818SMauro Carvalho Chehab pulse8->rx_msg[idx].len = pulse8->new_rx_msg_len;
421a8106818SMauro Carvalho Chehab spin_unlock_irqrestore(&pulse8->msg_lock, irq_flags);
422a8106818SMauro Carvalho Chehab schedule_work(&pulse8->irq_work);
423a8106818SMauro Carvalho Chehab pulse8->new_rx_msg_len = 0;
424a8106818SMauro Carvalho Chehab break;
425a8106818SMauro Carvalho Chehab case MSGCODE_TRANSMIT_SUCCEEDED:
426a8106818SMauro Carvalho Chehab WARN_ON(pulse8->tx_done_status);
427a8106818SMauro Carvalho Chehab pulse8->tx_done_status = CEC_TX_STATUS_OK;
428a8106818SMauro Carvalho Chehab schedule_work(&pulse8->irq_work);
429a8106818SMauro Carvalho Chehab break;
430a8106818SMauro Carvalho Chehab case MSGCODE_TRANSMIT_FAILED_ACK:
431a8106818SMauro Carvalho Chehab /*
432a8106818SMauro Carvalho Chehab * A NACK for a broadcast message makes no sense, these
433a8106818SMauro Carvalho Chehab * seem to be spurious messages and are skipped.
434a8106818SMauro Carvalho Chehab */
435a8106818SMauro Carvalho Chehab if (pulse8->tx_msg_is_bcast)
436a8106818SMauro Carvalho Chehab break;
437a8106818SMauro Carvalho Chehab WARN_ON(pulse8->tx_done_status);
438a8106818SMauro Carvalho Chehab pulse8->tx_done_status = CEC_TX_STATUS_NACK;
439a8106818SMauro Carvalho Chehab schedule_work(&pulse8->irq_work);
440a8106818SMauro Carvalho Chehab break;
441a8106818SMauro Carvalho Chehab case MSGCODE_TRANSMIT_FAILED_LINE:
442a8106818SMauro Carvalho Chehab case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
443a8106818SMauro Carvalho Chehab case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
444a8106818SMauro Carvalho Chehab WARN_ON(pulse8->tx_done_status);
445a8106818SMauro Carvalho Chehab pulse8->tx_done_status = CEC_TX_STATUS_ERROR;
446a8106818SMauro Carvalho Chehab schedule_work(&pulse8->irq_work);
447a8106818SMauro Carvalho Chehab break;
448a8106818SMauro Carvalho Chehab case MSGCODE_HIGH_ERROR:
449a8106818SMauro Carvalho Chehab case MSGCODE_LOW_ERROR:
450a8106818SMauro Carvalho Chehab case MSGCODE_RECEIVE_FAILED:
451a8106818SMauro Carvalho Chehab case MSGCODE_TIMEOUT_ERROR:
452a8106818SMauro Carvalho Chehab pulse8->new_rx_msg_len = 0;
453a8106818SMauro Carvalho Chehab break;
454a8106818SMauro Carvalho Chehab case MSGCODE_COMMAND_ACCEPTED:
455a8106818SMauro Carvalho Chehab case MSGCODE_COMMAND_REJECTED:
456a8106818SMauro Carvalho Chehab default:
457a8106818SMauro Carvalho Chehab if (pulse8->idx == 0)
458a8106818SMauro Carvalho Chehab break;
459a8106818SMauro Carvalho Chehab memcpy(pulse8->data, pulse8->buf, pulse8->idx);
460a8106818SMauro Carvalho Chehab pulse8->len = pulse8->idx;
461a8106818SMauro Carvalho Chehab complete(&pulse8->cmd_done);
462a8106818SMauro Carvalho Chehab break;
463a8106818SMauro Carvalho Chehab }
464a8106818SMauro Carvalho Chehab pulse8->idx = 0;
465a8106818SMauro Carvalho Chehab pulse8->started = false;
466a8106818SMauro Carvalho Chehab return IRQ_HANDLED;
467a8106818SMauro Carvalho Chehab } else if (data == MSGSTART) {
468a8106818SMauro Carvalho Chehab pulse8->idx = 0;
469a8106818SMauro Carvalho Chehab pulse8->started = true;
470a8106818SMauro Carvalho Chehab return IRQ_HANDLED;
471a8106818SMauro Carvalho Chehab }
472a8106818SMauro Carvalho Chehab
473a8106818SMauro Carvalho Chehab if (pulse8->idx >= DATA_SIZE) {
474a8106818SMauro Carvalho Chehab dev_dbg(pulse8->dev,
475a8106818SMauro Carvalho Chehab "throwing away %d bytes of garbage\n", pulse8->idx);
476a8106818SMauro Carvalho Chehab pulse8->idx = 0;
477a8106818SMauro Carvalho Chehab }
478a8106818SMauro Carvalho Chehab pulse8->buf[pulse8->idx++] = data;
479a8106818SMauro Carvalho Chehab return IRQ_HANDLED;
480a8106818SMauro Carvalho Chehab }
481a8106818SMauro Carvalho Chehab
pulse8_cec_adap_enable(struct cec_adapter * adap,bool enable)482a8106818SMauro Carvalho Chehab static int pulse8_cec_adap_enable(struct cec_adapter *adap, bool enable)
483a8106818SMauro Carvalho Chehab {
484a8106818SMauro Carvalho Chehab struct pulse8 *pulse8 = cec_get_drvdata(adap);
485a8106818SMauro Carvalho Chehab u8 cmd[16];
486a8106818SMauro Carvalho Chehab int err;
487a8106818SMauro Carvalho Chehab
488a8106818SMauro Carvalho Chehab mutex_lock(&pulse8->lock);
489a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_SET_CONTROLLED;
490a8106818SMauro Carvalho Chehab cmd[1] = enable;
491a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 2,
492a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 1);
493a8106818SMauro Carvalho Chehab if (!enable) {
494a8106818SMauro Carvalho Chehab pulse8->rx_msg_num = 0;
495a8106818SMauro Carvalho Chehab pulse8->tx_done_status = 0;
496a8106818SMauro Carvalho Chehab }
497a8106818SMauro Carvalho Chehab mutex_unlock(&pulse8->lock);
498a8106818SMauro Carvalho Chehab return enable ? err : 0;
499a8106818SMauro Carvalho Chehab }
500a8106818SMauro Carvalho Chehab
pulse8_cec_adap_log_addr(struct cec_adapter * adap,u8 log_addr)501a8106818SMauro Carvalho Chehab static int pulse8_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
502a8106818SMauro Carvalho Chehab {
503a8106818SMauro Carvalho Chehab struct pulse8 *pulse8 = cec_get_drvdata(adap);
504a8106818SMauro Carvalho Chehab u16 mask = 0;
505a8106818SMauro Carvalho Chehab u16 pa = adap->phys_addr;
506a8106818SMauro Carvalho Chehab u8 cmd[16];
507a8106818SMauro Carvalho Chehab int err = 0;
508a8106818SMauro Carvalho Chehab
509a8106818SMauro Carvalho Chehab mutex_lock(&pulse8->lock);
510a8106818SMauro Carvalho Chehab if (log_addr != CEC_LOG_ADDR_INVALID)
511a8106818SMauro Carvalho Chehab mask = 1 << log_addr;
512a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_SET_ACK_MASK;
513a8106818SMauro Carvalho Chehab cmd[1] = mask >> 8;
514a8106818SMauro Carvalho Chehab cmd[2] = mask & 0xff;
515a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 3,
516a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 0);
517a8106818SMauro Carvalho Chehab if ((err && mask != 0) || pulse8->restoring_config)
518a8106818SMauro Carvalho Chehab goto unlock;
519a8106818SMauro Carvalho Chehab
520a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_SET_AUTO_ENABLED;
521a8106818SMauro Carvalho Chehab cmd[1] = log_addr == CEC_LOG_ADDR_INVALID ? 0 : 1;
522a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 2,
523a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 0);
524a8106818SMauro Carvalho Chehab if (err)
525a8106818SMauro Carvalho Chehab goto unlock;
526a8106818SMauro Carvalho Chehab pulse8->autonomous = cmd[1];
527a8106818SMauro Carvalho Chehab if (log_addr == CEC_LOG_ADDR_INVALID)
528a8106818SMauro Carvalho Chehab goto unlock;
529a8106818SMauro Carvalho Chehab
530a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_SET_DEVICE_TYPE;
531a8106818SMauro Carvalho Chehab cmd[1] = adap->log_addrs.primary_device_type[0];
532a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 2,
533a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 0);
534a8106818SMauro Carvalho Chehab if (err)
535a8106818SMauro Carvalho Chehab goto unlock;
536a8106818SMauro Carvalho Chehab
537a8106818SMauro Carvalho Chehab switch (adap->log_addrs.primary_device_type[0]) {
538a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_TV:
539a8106818SMauro Carvalho Chehab mask = CEC_LOG_ADDR_MASK_TV;
540a8106818SMauro Carvalho Chehab break;
541a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_RECORD:
542a8106818SMauro Carvalho Chehab mask = CEC_LOG_ADDR_MASK_RECORD;
543a8106818SMauro Carvalho Chehab break;
544a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_TUNER:
545a8106818SMauro Carvalho Chehab mask = CEC_LOG_ADDR_MASK_TUNER;
546a8106818SMauro Carvalho Chehab break;
547a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_PLAYBACK:
548a8106818SMauro Carvalho Chehab mask = CEC_LOG_ADDR_MASK_PLAYBACK;
549a8106818SMauro Carvalho Chehab break;
550a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM:
551a8106818SMauro Carvalho Chehab mask = CEC_LOG_ADDR_MASK_AUDIOSYSTEM;
552a8106818SMauro Carvalho Chehab break;
553a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_SWITCH:
554a8106818SMauro Carvalho Chehab mask = CEC_LOG_ADDR_MASK_UNREGISTERED;
555a8106818SMauro Carvalho Chehab break;
556a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_PROCESSOR:
557a8106818SMauro Carvalho Chehab mask = CEC_LOG_ADDR_MASK_SPECIFIC;
558a8106818SMauro Carvalho Chehab break;
559a8106818SMauro Carvalho Chehab default:
560a8106818SMauro Carvalho Chehab mask = 0;
561a8106818SMauro Carvalho Chehab break;
562a8106818SMauro Carvalho Chehab }
563a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_SET_LOGICAL_ADDRESS_MASK;
564a8106818SMauro Carvalho Chehab cmd[1] = mask >> 8;
565a8106818SMauro Carvalho Chehab cmd[2] = mask & 0xff;
566a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 3,
567a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 0);
568a8106818SMauro Carvalho Chehab if (err)
569a8106818SMauro Carvalho Chehab goto unlock;
570a8106818SMauro Carvalho Chehab
571a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS;
572a8106818SMauro Carvalho Chehab cmd[1] = log_addr;
573a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 2,
574a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 0);
575a8106818SMauro Carvalho Chehab if (err)
576a8106818SMauro Carvalho Chehab goto unlock;
577a8106818SMauro Carvalho Chehab
578a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_SET_PHYSICAL_ADDRESS;
579a8106818SMauro Carvalho Chehab cmd[1] = pa >> 8;
580a8106818SMauro Carvalho Chehab cmd[2] = pa & 0xff;
581a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 3,
582a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 0);
583a8106818SMauro Carvalho Chehab if (err)
584a8106818SMauro Carvalho Chehab goto unlock;
585a8106818SMauro Carvalho Chehab
58645ba1c0bSHans Verkuil if (pulse8->vers < 10) {
587a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_SET_HDMI_VERSION;
588a8106818SMauro Carvalho Chehab cmd[1] = adap->log_addrs.cec_version;
589a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 2,
590a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 0);
591a8106818SMauro Carvalho Chehab if (err)
592a8106818SMauro Carvalho Chehab goto unlock;
59345ba1c0bSHans Verkuil }
594a8106818SMauro Carvalho Chehab
595a8106818SMauro Carvalho Chehab if (adap->log_addrs.osd_name[0]) {
596a8106818SMauro Carvalho Chehab size_t osd_len = strlen(adap->log_addrs.osd_name);
597a8106818SMauro Carvalho Chehab char *osd_str = cmd + 1;
598a8106818SMauro Carvalho Chehab
599a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_SET_OSD_NAME;
600a8106818SMauro Carvalho Chehab strscpy(cmd + 1, adap->log_addrs.osd_name, sizeof(cmd) - 1);
601a8106818SMauro Carvalho Chehab if (osd_len < 4) {
602a8106818SMauro Carvalho Chehab memset(osd_str + osd_len, ' ', 4 - osd_len);
603a8106818SMauro Carvalho Chehab osd_len = 4;
604a8106818SMauro Carvalho Chehab osd_str[osd_len] = '\0';
605a8106818SMauro Carvalho Chehab strscpy(adap->log_addrs.osd_name, osd_str,
606a8106818SMauro Carvalho Chehab sizeof(adap->log_addrs.osd_name));
607a8106818SMauro Carvalho Chehab }
608a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 1 + osd_len,
609a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 0);
610a8106818SMauro Carvalho Chehab if (err)
611a8106818SMauro Carvalho Chehab goto unlock;
612a8106818SMauro Carvalho Chehab }
613a8106818SMauro Carvalho Chehab
614a8106818SMauro Carvalho Chehab unlock:
615a8106818SMauro Carvalho Chehab if (pulse8->restoring_config)
616a8106818SMauro Carvalho Chehab pulse8->restoring_config = false;
617a8106818SMauro Carvalho Chehab else
618a8106818SMauro Carvalho Chehab pulse8->config_pending = true;
619a8106818SMauro Carvalho Chehab mutex_unlock(&pulse8->lock);
620a8106818SMauro Carvalho Chehab return log_addr == CEC_LOG_ADDR_INVALID ? 0 : err;
621a8106818SMauro Carvalho Chehab }
622a8106818SMauro Carvalho Chehab
pulse8_cec_adap_transmit(struct cec_adapter * adap,u8 attempts,u32 signal_free_time,struct cec_msg * msg)623a8106818SMauro Carvalho Chehab static int pulse8_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
624a8106818SMauro Carvalho Chehab u32 signal_free_time, struct cec_msg *msg)
625a8106818SMauro Carvalho Chehab {
626a8106818SMauro Carvalho Chehab struct pulse8 *pulse8 = cec_get_drvdata(adap);
627a8106818SMauro Carvalho Chehab
628a8106818SMauro Carvalho Chehab pulse8->tx_msg = *msg;
629a8106818SMauro Carvalho Chehab if (debug)
630a8106818SMauro Carvalho Chehab dev_info(pulse8->dev, "adap transmit %*ph\n",
631a8106818SMauro Carvalho Chehab msg->len, msg->msg);
632a8106818SMauro Carvalho Chehab pulse8->tx_signal_free_time = signal_free_time;
633a8106818SMauro Carvalho Chehab schedule_work(&pulse8->tx_work);
634a8106818SMauro Carvalho Chehab return 0;
635a8106818SMauro Carvalho Chehab }
636a8106818SMauro Carvalho Chehab
pulse8_cec_adap_free(struct cec_adapter * adap)637a8106818SMauro Carvalho Chehab static void pulse8_cec_adap_free(struct cec_adapter *adap)
638a8106818SMauro Carvalho Chehab {
639a8106818SMauro Carvalho Chehab struct pulse8 *pulse8 = cec_get_drvdata(adap);
640a8106818SMauro Carvalho Chehab
641a8106818SMauro Carvalho Chehab cancel_delayed_work_sync(&pulse8->ping_eeprom_work);
642a8106818SMauro Carvalho Chehab cancel_work_sync(&pulse8->irq_work);
643a8106818SMauro Carvalho Chehab cancel_work_sync(&pulse8->tx_work);
644a8106818SMauro Carvalho Chehab kfree(pulse8);
645a8106818SMauro Carvalho Chehab }
646a8106818SMauro Carvalho Chehab
647a8106818SMauro Carvalho Chehab static const struct cec_adap_ops pulse8_cec_adap_ops = {
648a8106818SMauro Carvalho Chehab .adap_enable = pulse8_cec_adap_enable,
649a8106818SMauro Carvalho Chehab .adap_log_addr = pulse8_cec_adap_log_addr,
650a8106818SMauro Carvalho Chehab .adap_transmit = pulse8_cec_adap_transmit,
651a8106818SMauro Carvalho Chehab .adap_free = pulse8_cec_adap_free,
652a8106818SMauro Carvalho Chehab };
653a8106818SMauro Carvalho Chehab
pulse8_disconnect(struct serio * serio)654a8106818SMauro Carvalho Chehab static void pulse8_disconnect(struct serio *serio)
655a8106818SMauro Carvalho Chehab {
656a8106818SMauro Carvalho Chehab struct pulse8 *pulse8 = serio_get_drvdata(serio);
657a8106818SMauro Carvalho Chehab
658a8106818SMauro Carvalho Chehab cec_unregister_adapter(pulse8->adap);
659a8106818SMauro Carvalho Chehab serio_set_drvdata(serio, NULL);
660a8106818SMauro Carvalho Chehab serio_close(serio);
661a8106818SMauro Carvalho Chehab }
662a8106818SMauro Carvalho Chehab
pulse8_setup(struct pulse8 * pulse8,struct serio * serio,struct cec_log_addrs * log_addrs,u16 * pa)663a8106818SMauro Carvalho Chehab static int pulse8_setup(struct pulse8 *pulse8, struct serio *serio,
664a8106818SMauro Carvalho Chehab struct cec_log_addrs *log_addrs, u16 *pa)
665a8106818SMauro Carvalho Chehab {
666a8106818SMauro Carvalho Chehab u8 *data = pulse8->data + 1;
667a8106818SMauro Carvalho Chehab u8 cmd[2];
668a8106818SMauro Carvalho Chehab int err;
669a8106818SMauro Carvalho Chehab time64_t date;
670a8106818SMauro Carvalho Chehab
671a8106818SMauro Carvalho Chehab pulse8->vers = 0;
672a8106818SMauro Carvalho Chehab
673a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_FIRMWARE_VERSION;
674a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2);
675a8106818SMauro Carvalho Chehab if (err)
676a8106818SMauro Carvalho Chehab return err;
677a8106818SMauro Carvalho Chehab pulse8->vers = (data[0] << 8) | data[1];
678a8106818SMauro Carvalho Chehab dev_info(pulse8->dev, "Firmware version %04x\n", pulse8->vers);
679a8106818SMauro Carvalho Chehab if (pulse8->vers < 2) {
680a8106818SMauro Carvalho Chehab *pa = CEC_PHYS_ADDR_INVALID;
681a8106818SMauro Carvalho Chehab return 0;
682a8106818SMauro Carvalho Chehab }
683a8106818SMauro Carvalho Chehab
684a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_GET_BUILDDATE;
685a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 4);
686a8106818SMauro Carvalho Chehab if (err)
687a8106818SMauro Carvalho Chehab return err;
688a8106818SMauro Carvalho Chehab date = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
689a98f670eSLinus Torvalds dev_info(pulse8->dev, "Firmware build date %ptT\n", &date);
690a8106818SMauro Carvalho Chehab
691a8106818SMauro Carvalho Chehab dev_dbg(pulse8->dev, "Persistent config:\n");
692a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_GET_AUTO_ENABLED;
693a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);
694a8106818SMauro Carvalho Chehab if (err)
695a8106818SMauro Carvalho Chehab return err;
696a8106818SMauro Carvalho Chehab pulse8->autonomous = data[0];
697a8106818SMauro Carvalho Chehab dev_dbg(pulse8->dev, "Autonomous mode: %s",
698a8106818SMauro Carvalho Chehab data[0] ? "on" : "off");
699a8106818SMauro Carvalho Chehab
70045ba1c0bSHans Verkuil if (pulse8->vers >= 10) {
70145ba1c0bSHans Verkuil cmd[0] = MSGCODE_GET_AUTO_POWER_ON;
70245ba1c0bSHans Verkuil err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);
70345ba1c0bSHans Verkuil if (!err)
70445ba1c0bSHans Verkuil dev_dbg(pulse8->dev, "Auto Power On: %s",
70545ba1c0bSHans Verkuil data[0] ? "on" : "off");
70645ba1c0bSHans Verkuil }
70745ba1c0bSHans Verkuil
708a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_GET_DEVICE_TYPE;
709a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);
710a8106818SMauro Carvalho Chehab if (err)
711a8106818SMauro Carvalho Chehab return err;
712a8106818SMauro Carvalho Chehab log_addrs->primary_device_type[0] = data[0];
713a8106818SMauro Carvalho Chehab dev_dbg(pulse8->dev, "Primary device type: %d\n", data[0]);
714a8106818SMauro Carvalho Chehab switch (log_addrs->primary_device_type[0]) {
715a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_TV:
716a8106818SMauro Carvalho Chehab log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TV;
717a8106818SMauro Carvalho Chehab log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_TV;
718a8106818SMauro Carvalho Chehab break;
719a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_RECORD:
720a8106818SMauro Carvalho Chehab log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_RECORD;
721a8106818SMauro Carvalho Chehab log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_RECORD;
722a8106818SMauro Carvalho Chehab break;
723a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_TUNER:
724a8106818SMauro Carvalho Chehab log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_TUNER;
725a8106818SMauro Carvalho Chehab log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_TUNER;
726a8106818SMauro Carvalho Chehab break;
727a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_PLAYBACK:
728a8106818SMauro Carvalho Chehab log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK;
729a8106818SMauro Carvalho Chehab log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_PLAYBACK;
730a8106818SMauro Carvalho Chehab break;
731a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM:
732a8106818SMauro Carvalho Chehab log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_PLAYBACK;
733a8106818SMauro Carvalho Chehab log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
734a8106818SMauro Carvalho Chehab break;
735a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_SWITCH:
736a8106818SMauro Carvalho Chehab log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED;
737a8106818SMauro Carvalho Chehab log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH;
738a8106818SMauro Carvalho Chehab break;
739a8106818SMauro Carvalho Chehab case CEC_OP_PRIM_DEVTYPE_PROCESSOR:
740a8106818SMauro Carvalho Chehab log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_SPECIFIC;
741a8106818SMauro Carvalho Chehab log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH;
742a8106818SMauro Carvalho Chehab break;
743a8106818SMauro Carvalho Chehab default:
744a8106818SMauro Carvalho Chehab log_addrs->log_addr_type[0] = CEC_LOG_ADDR_TYPE_UNREGISTERED;
745a8106818SMauro Carvalho Chehab log_addrs->all_device_types[0] = CEC_OP_ALL_DEVTYPE_SWITCH;
746a8106818SMauro Carvalho Chehab dev_info(pulse8->dev, "Unknown Primary Device Type: %d\n",
747a8106818SMauro Carvalho Chehab log_addrs->primary_device_type[0]);
748a8106818SMauro Carvalho Chehab break;
749a8106818SMauro Carvalho Chehab }
750a8106818SMauro Carvalho Chehab
751a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_GET_LOGICAL_ADDRESS_MASK;
752a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 2);
753a8106818SMauro Carvalho Chehab if (err)
754a8106818SMauro Carvalho Chehab return err;
755a8106818SMauro Carvalho Chehab log_addrs->log_addr_mask = (data[0] << 8) | data[1];
756a8106818SMauro Carvalho Chehab dev_dbg(pulse8->dev, "Logical address ACK mask: %x\n",
757a8106818SMauro Carvalho Chehab log_addrs->log_addr_mask);
758a8106818SMauro Carvalho Chehab if (log_addrs->log_addr_mask)
759a8106818SMauro Carvalho Chehab log_addrs->num_log_addrs = 1;
760a8106818SMauro Carvalho Chehab
761a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_GET_PHYSICAL_ADDRESS;
762a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);
763a8106818SMauro Carvalho Chehab if (err)
764a8106818SMauro Carvalho Chehab return err;
765a8106818SMauro Carvalho Chehab *pa = (data[0] << 8) | data[1];
766a8106818SMauro Carvalho Chehab dev_dbg(pulse8->dev, "Physical address: %x.%x.%x.%x\n",
767a8106818SMauro Carvalho Chehab cec_phys_addr_exp(*pa));
768a8106818SMauro Carvalho Chehab
76945ba1c0bSHans Verkuil log_addrs->cec_version = CEC_OP_CEC_VERSION_1_4;
77045ba1c0bSHans Verkuil if (pulse8->vers < 10) {
771a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_GET_HDMI_VERSION;
772a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 1);
773a8106818SMauro Carvalho Chehab if (err)
774a8106818SMauro Carvalho Chehab return err;
775a8106818SMauro Carvalho Chehab log_addrs->cec_version = data[0];
776a8106818SMauro Carvalho Chehab dev_dbg(pulse8->dev, "CEC version: %d\n", log_addrs->cec_version);
77745ba1c0bSHans Verkuil }
778a8106818SMauro Carvalho Chehab
779a8106818SMauro Carvalho Chehab cmd[0] = MSGCODE_GET_OSD_NAME;
780a8106818SMauro Carvalho Chehab err = pulse8_send_and_wait(pulse8, cmd, 1, cmd[0], 0);
781a8106818SMauro Carvalho Chehab if (err)
782a8106818SMauro Carvalho Chehab return err;
783a8106818SMauro Carvalho Chehab strscpy(log_addrs->osd_name, data, sizeof(log_addrs->osd_name));
784a8106818SMauro Carvalho Chehab dev_dbg(pulse8->dev, "OSD name: %s\n", log_addrs->osd_name);
785a8106818SMauro Carvalho Chehab
786a8106818SMauro Carvalho Chehab return 0;
787a8106818SMauro Carvalho Chehab }
788a8106818SMauro Carvalho Chehab
pulse8_apply_persistent_config(struct pulse8 * pulse8,struct cec_log_addrs * log_addrs,u16 pa)789a8106818SMauro Carvalho Chehab static int pulse8_apply_persistent_config(struct pulse8 *pulse8,
790a8106818SMauro Carvalho Chehab struct cec_log_addrs *log_addrs,
791a8106818SMauro Carvalho Chehab u16 pa)
792a8106818SMauro Carvalho Chehab {
793a8106818SMauro Carvalho Chehab int err;
794a8106818SMauro Carvalho Chehab
795a8106818SMauro Carvalho Chehab err = cec_s_log_addrs(pulse8->adap, log_addrs, false);
796a8106818SMauro Carvalho Chehab if (err)
797a8106818SMauro Carvalho Chehab return err;
798a8106818SMauro Carvalho Chehab
799a8106818SMauro Carvalho Chehab cec_s_phys_addr(pulse8->adap, pa, false);
800a8106818SMauro Carvalho Chehab
801a8106818SMauro Carvalho Chehab return 0;
802a8106818SMauro Carvalho Chehab }
803a8106818SMauro Carvalho Chehab
pulse8_ping_eeprom_work_handler(struct work_struct * work)804a8106818SMauro Carvalho Chehab static void pulse8_ping_eeprom_work_handler(struct work_struct *work)
805a8106818SMauro Carvalho Chehab {
806a8106818SMauro Carvalho Chehab struct pulse8 *pulse8 =
807a8106818SMauro Carvalho Chehab container_of(work, struct pulse8, ping_eeprom_work.work);
808a8106818SMauro Carvalho Chehab u8 cmd;
809a8106818SMauro Carvalho Chehab
810a8106818SMauro Carvalho Chehab mutex_lock(&pulse8->lock);
811a8106818SMauro Carvalho Chehab cmd = MSGCODE_PING;
812*92cbf865SDmitry Antipov if (pulse8_send_and_wait(pulse8, &cmd, 1,
813*92cbf865SDmitry Antipov MSGCODE_COMMAND_ACCEPTED, 0)) {
814*92cbf865SDmitry Antipov dev_warn(pulse8->dev, "failed to ping EEPROM\n");
815*92cbf865SDmitry Antipov goto unlock;
816*92cbf865SDmitry Antipov }
817a8106818SMauro Carvalho Chehab
818a8106818SMauro Carvalho Chehab if (pulse8->vers < 2)
819a8106818SMauro Carvalho Chehab goto unlock;
820a8106818SMauro Carvalho Chehab
821a8106818SMauro Carvalho Chehab if (pulse8->config_pending && persistent_config) {
822a8106818SMauro Carvalho Chehab dev_dbg(pulse8->dev, "writing pending config to EEPROM\n");
823a8106818SMauro Carvalho Chehab cmd = MSGCODE_WRITE_EEPROM;
824a8106818SMauro Carvalho Chehab if (pulse8_send_and_wait(pulse8, &cmd, 1,
825a8106818SMauro Carvalho Chehab MSGCODE_COMMAND_ACCEPTED, 0))
826a8106818SMauro Carvalho Chehab dev_info(pulse8->dev, "failed to write pending config to EEPROM\n");
827a8106818SMauro Carvalho Chehab else
828a8106818SMauro Carvalho Chehab pulse8->config_pending = false;
829a8106818SMauro Carvalho Chehab }
830a8106818SMauro Carvalho Chehab unlock:
831a8106818SMauro Carvalho Chehab schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD);
832a8106818SMauro Carvalho Chehab mutex_unlock(&pulse8->lock);
833a8106818SMauro Carvalho Chehab }
834a8106818SMauro Carvalho Chehab
pulse8_connect(struct serio * serio,struct serio_driver * drv)835a8106818SMauro Carvalho Chehab static int pulse8_connect(struct serio *serio, struct serio_driver *drv)
836a8106818SMauro Carvalho Chehab {
837a8106818SMauro Carvalho Chehab u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL;
838a8106818SMauro Carvalho Chehab struct pulse8 *pulse8;
839a8106818SMauro Carvalho Chehab int err = -ENOMEM;
840a8106818SMauro Carvalho Chehab struct cec_log_addrs log_addrs = {};
841a8106818SMauro Carvalho Chehab u16 pa = CEC_PHYS_ADDR_INVALID;
842a8106818SMauro Carvalho Chehab
843a8106818SMauro Carvalho Chehab pulse8 = kzalloc(sizeof(*pulse8), GFP_KERNEL);
844a8106818SMauro Carvalho Chehab
845a8106818SMauro Carvalho Chehab if (!pulse8)
846a8106818SMauro Carvalho Chehab return -ENOMEM;
847a8106818SMauro Carvalho Chehab
848a8106818SMauro Carvalho Chehab pulse8->serio = serio;
849a8106818SMauro Carvalho Chehab pulse8->adap = cec_allocate_adapter(&pulse8_cec_adap_ops, pulse8,
850a8106818SMauro Carvalho Chehab dev_name(&serio->dev), caps, 1);
851a8106818SMauro Carvalho Chehab err = PTR_ERR_OR_ZERO(pulse8->adap);
852024e01deSHans Verkuil if (err < 0) {
853024e01deSHans Verkuil kfree(pulse8);
854024e01deSHans Verkuil return err;
855024e01deSHans Verkuil }
856a8106818SMauro Carvalho Chehab
857a8106818SMauro Carvalho Chehab pulse8->dev = &serio->dev;
858a8106818SMauro Carvalho Chehab serio_set_drvdata(serio, pulse8);
859a8106818SMauro Carvalho Chehab INIT_WORK(&pulse8->irq_work, pulse8_irq_work_handler);
860a8106818SMauro Carvalho Chehab INIT_WORK(&pulse8->tx_work, pulse8_tx_work_handler);
861a8106818SMauro Carvalho Chehab INIT_DELAYED_WORK(&pulse8->ping_eeprom_work,
862a8106818SMauro Carvalho Chehab pulse8_ping_eeprom_work_handler);
863a8106818SMauro Carvalho Chehab mutex_init(&pulse8->lock);
864a8106818SMauro Carvalho Chehab spin_lock_init(&pulse8->msg_lock);
865a8106818SMauro Carvalho Chehab pulse8->config_pending = false;
866a8106818SMauro Carvalho Chehab
867a8106818SMauro Carvalho Chehab err = serio_open(serio, drv);
868a8106818SMauro Carvalho Chehab if (err)
869a8106818SMauro Carvalho Chehab goto delete_adap;
870a8106818SMauro Carvalho Chehab
871a8106818SMauro Carvalho Chehab err = pulse8_setup(pulse8, serio, &log_addrs, &pa);
872a8106818SMauro Carvalho Chehab if (err)
873a8106818SMauro Carvalho Chehab goto close_serio;
874a8106818SMauro Carvalho Chehab
875a8106818SMauro Carvalho Chehab err = cec_register_adapter(pulse8->adap, &serio->dev);
876a8106818SMauro Carvalho Chehab if (err < 0)
877a8106818SMauro Carvalho Chehab goto close_serio;
878a8106818SMauro Carvalho Chehab
879a8106818SMauro Carvalho Chehab pulse8->dev = &pulse8->adap->devnode.dev;
880a8106818SMauro Carvalho Chehab
881a8106818SMauro Carvalho Chehab if (persistent_config && pulse8->autonomous) {
882a8106818SMauro Carvalho Chehab err = pulse8_apply_persistent_config(pulse8, &log_addrs, pa);
883a8106818SMauro Carvalho Chehab if (err)
884a8106818SMauro Carvalho Chehab goto close_serio;
885a8106818SMauro Carvalho Chehab pulse8->restoring_config = true;
886a8106818SMauro Carvalho Chehab }
887a8106818SMauro Carvalho Chehab
888a8106818SMauro Carvalho Chehab schedule_delayed_work(&pulse8->ping_eeprom_work, PING_PERIOD);
889a8106818SMauro Carvalho Chehab
890a8106818SMauro Carvalho Chehab return 0;
891a8106818SMauro Carvalho Chehab
892a8106818SMauro Carvalho Chehab close_serio:
893a8106818SMauro Carvalho Chehab pulse8->serio = NULL;
894a8106818SMauro Carvalho Chehab serio_set_drvdata(serio, NULL);
895a8106818SMauro Carvalho Chehab serio_close(serio);
896a8106818SMauro Carvalho Chehab delete_adap:
897a8106818SMauro Carvalho Chehab cec_delete_adapter(pulse8->adap);
898a8106818SMauro Carvalho Chehab return err;
899a8106818SMauro Carvalho Chehab }
900a8106818SMauro Carvalho Chehab
901a8106818SMauro Carvalho Chehab static const struct serio_device_id pulse8_serio_ids[] = {
902a8106818SMauro Carvalho Chehab {
903a8106818SMauro Carvalho Chehab .type = SERIO_RS232,
904a8106818SMauro Carvalho Chehab .proto = SERIO_PULSE8_CEC,
905a8106818SMauro Carvalho Chehab .id = SERIO_ANY,
906a8106818SMauro Carvalho Chehab .extra = SERIO_ANY,
907a8106818SMauro Carvalho Chehab },
908a8106818SMauro Carvalho Chehab { 0 }
909a8106818SMauro Carvalho Chehab };
910a8106818SMauro Carvalho Chehab
911a8106818SMauro Carvalho Chehab MODULE_DEVICE_TABLE(serio, pulse8_serio_ids);
912a8106818SMauro Carvalho Chehab
913a8106818SMauro Carvalho Chehab static struct serio_driver pulse8_drv = {
914a8106818SMauro Carvalho Chehab .driver = {
915a8106818SMauro Carvalho Chehab .name = "pulse8-cec",
916a8106818SMauro Carvalho Chehab },
917a8106818SMauro Carvalho Chehab .description = "Pulse Eight HDMI CEC driver",
918a8106818SMauro Carvalho Chehab .id_table = pulse8_serio_ids,
919a8106818SMauro Carvalho Chehab .interrupt = pulse8_interrupt,
920a8106818SMauro Carvalho Chehab .connect = pulse8_connect,
921a8106818SMauro Carvalho Chehab .disconnect = pulse8_disconnect,
922a8106818SMauro Carvalho Chehab };
923a8106818SMauro Carvalho Chehab
924a8106818SMauro Carvalho Chehab module_serio_driver(pulse8_drv);
925