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