xref: /linux/drivers/media/cec/usb/extron-da-hd-4k-plus/cec-splitter.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 /*
4  * Copyright 2021-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
5  */
6 
7 #include <media/cec.h>
8 
9 #include "cec-splitter.h"
10 
11 /*
12  * Helper function to reply to a received message with a Feature Abort
13  * message.
14  */
cec_feature_abort_reason(struct cec_adapter * adap,struct cec_msg * msg,u8 reason)15 static int cec_feature_abort_reason(struct cec_adapter *adap,
16 				    struct cec_msg *msg, u8 reason)
17 {
18 	struct cec_msg tx_msg = { };
19 
20 	/*
21 	 * Don't reply with CEC_MSG_FEATURE_ABORT to a CEC_MSG_FEATURE_ABORT
22 	 * message!
23 	 */
24 	if (msg->msg[1] == CEC_MSG_FEATURE_ABORT)
25 		return 0;
26 	/* Don't Feature Abort messages from 'Unregistered' */
27 	if (cec_msg_initiator(msg) == CEC_LOG_ADDR_UNREGISTERED)
28 		return 0;
29 	cec_msg_set_reply_to(&tx_msg, msg);
30 	cec_msg_feature_abort(&tx_msg, msg->msg[1], reason);
31 	return cec_transmit_msg(adap, &tx_msg, false);
32 }
33 
34 /* Transmit an Active Source message from this output port to a sink */
cec_port_out_active_source(struct cec_splitter_port * p)35 static void cec_port_out_active_source(struct cec_splitter_port *p)
36 {
37 	struct cec_adapter *adap = p->adap;
38 	struct cec_msg msg;
39 
40 	if (!adap->is_configured)
41 		return;
42 	p->is_active_source = true;
43 	cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0);
44 	cec_msg_active_source(&msg, adap->phys_addr);
45 	cec_transmit_msg(adap, &msg, false);
46 }
47 
48 /* Transmit Active Source messages from all output ports to the sinks */
cec_out_active_source(struct cec_splitter * splitter)49 static void cec_out_active_source(struct cec_splitter *splitter)
50 {
51 	unsigned int i;
52 
53 	for (i = 0; i < splitter->num_out_ports; i++)
54 		cec_port_out_active_source(splitter->ports[i]);
55 }
56 
57 /* Transmit a Standby message from this output port to a sink */
cec_port_out_standby(struct cec_splitter_port * p)58 static void cec_port_out_standby(struct cec_splitter_port *p)
59 {
60 	struct cec_adapter *adap = p->adap;
61 	struct cec_msg msg;
62 
63 	if (!adap->is_configured)
64 		return;
65 	cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0);
66 	cec_msg_standby(&msg);
67 	cec_transmit_msg(adap, &msg, false);
68 }
69 
70 /* Transmit Standby messages from all output ports to the sinks */
cec_out_standby(struct cec_splitter * splitter)71 static void cec_out_standby(struct cec_splitter *splitter)
72 {
73 	unsigned int i;
74 
75 	for (i = 0; i < splitter->num_out_ports; i++)
76 		cec_port_out_standby(splitter->ports[i]);
77 }
78 
79 /* Transmit an Image/Text View On message from this output port to a sink */
cec_port_out_wakeup(struct cec_splitter_port * p,u8 opcode)80 static void cec_port_out_wakeup(struct cec_splitter_port *p, u8 opcode)
81 {
82 	struct cec_adapter *adap = p->adap;
83 	u8 la = adap->log_addrs.log_addr[0];
84 	struct cec_msg msg;
85 
86 	if (la == CEC_LOG_ADDR_INVALID)
87 		la = CEC_LOG_ADDR_UNREGISTERED;
88 	cec_msg_init(&msg, la, 0);
89 	msg.len = 2;
90 	msg.msg[1] = opcode;
91 	cec_transmit_msg(adap, &msg, false);
92 }
93 
94 /* Transmit Image/Text View On messages from all output ports to the sinks */
cec_out_wakeup(struct cec_splitter * splitter,u8 opcode)95 static void cec_out_wakeup(struct cec_splitter *splitter, u8 opcode)
96 {
97 	unsigned int i;
98 
99 	for (i = 0; i < splitter->num_out_ports; i++)
100 		cec_port_out_wakeup(splitter->ports[i], opcode);
101 }
102 
103 /*
104  * Update the power state of the unconfigured CEC device to either
105  * Off or On depending on the current state of the splitter.
106  * This keeps the outputs in a consistent state.
107  */
cec_splitter_unconfigured_output(struct cec_splitter_port * p)108 void cec_splitter_unconfigured_output(struct cec_splitter_port *p)
109 {
110 	p->video_latency = 1;
111 	p->power_status = p->splitter->is_standby ?
112 		CEC_OP_POWER_STATUS_TO_STANDBY : CEC_OP_POWER_STATUS_TO_ON;
113 
114 	/* The adapter was unconfigured, so clear the sequence and ts values */
115 	p->out_give_device_power_status_seq = 0;
116 	p->out_give_device_power_status_ts = ktime_set(0, 0);
117 	p->out_request_current_latency_seq = 0;
118 	p->out_request_current_latency_ts = ktime_set(0, 0);
119 }
120 
121 /*
122  * Update the power state of the newly configured CEC device to either
123  * Off or On depending on the current state of the splitter.
124  * This keeps the outputs in a consistent state.
125  */
cec_splitter_configured_output(struct cec_splitter_port * p)126 void cec_splitter_configured_output(struct cec_splitter_port *p)
127 {
128 	p->video_latency = 1;
129 	p->power_status = p->splitter->is_standby ?
130 		CEC_OP_POWER_STATUS_TO_STANDBY : CEC_OP_POWER_STATUS_TO_ON;
131 
132 	if (p->splitter->is_standby) {
133 		/*
134 		 * Some sinks only obey Standby if it comes from the
135 		 * active source.
136 		 */
137 		cec_port_out_active_source(p);
138 		cec_port_out_standby(p);
139 	} else {
140 		cec_port_out_wakeup(p, CEC_MSG_IMAGE_VIEW_ON);
141 	}
142 }
143 
144 /* Pass the in_msg on to all output ports */
cec_out_passthrough(struct cec_splitter * splitter,const struct cec_msg * in_msg)145 static void cec_out_passthrough(struct cec_splitter *splitter,
146 				const struct cec_msg *in_msg)
147 {
148 	unsigned int i;
149 
150 	for (i = 0; i < splitter->num_out_ports; i++) {
151 		struct cec_splitter_port *p = splitter->ports[i];
152 		struct cec_adapter *adap = p->adap;
153 		struct cec_msg msg;
154 
155 		if (!adap->is_configured)
156 			continue;
157 		cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0);
158 		msg.len = in_msg->len;
159 		memcpy(msg.msg + 1, in_msg->msg + 1, msg.len - 1);
160 		cec_transmit_msg(adap, &msg, false);
161 	}
162 }
163 
164 /*
165  * See if all output ports received the Report Current Latency message,
166  * and if so, transmit the result from the input port to the video source.
167  */
cec_out_report_current_latency(struct cec_splitter * splitter,struct cec_adapter * input_adap)168 static void cec_out_report_current_latency(struct cec_splitter *splitter,
169 					   struct cec_adapter *input_adap)
170 {
171 	struct cec_msg reply = {};
172 	unsigned int reply_lat = 0;
173 	unsigned int cnt = 0;
174 	unsigned int i;
175 
176 	for (i = 0; i < splitter->num_out_ports; i++) {
177 		struct cec_splitter_port *p = splitter->ports[i];
178 		struct cec_adapter *adap = p->adap;
179 
180 		/* Skip unconfigured ports */
181 		if (!adap->is_configured)
182 			continue;
183 		/* Return if a port is still waiting for a reply */
184 		if (p->out_request_current_latency_seq)
185 			return;
186 		reply_lat += p->video_latency - 1;
187 		cnt++;
188 	}
189 
190 	/*
191 	 * All ports that can reply, replied, so clear the sequence
192 	 * and timestamp values.
193 	 */
194 	for (i = 0; i < splitter->num_out_ports; i++) {
195 		struct cec_splitter_port *p = splitter->ports[i];
196 
197 		p->out_request_current_latency_seq = 0;
198 		p->out_request_current_latency_ts = ktime_set(0, 0);
199 	}
200 
201 	/*
202 	 * Return if there were no replies or the input port is no longer
203 	 * configured.
204 	 */
205 	if (!cnt || !input_adap->is_configured)
206 		return;
207 
208 	/* Reply with the average latency */
209 	reply_lat = 1 + reply_lat / cnt;
210 	cec_msg_init(&reply, input_adap->log_addrs.log_addr[0],
211 		     splitter->request_current_latency_dest);
212 	cec_msg_report_current_latency(&reply, input_adap->phys_addr,
213 				       reply_lat, 1, 1, 1);
214 	cec_transmit_msg(input_adap, &reply, false);
215 }
216 
217 /* Transmit Request Current Latency to all output ports */
cec_out_request_current_latency(struct cec_splitter * splitter)218 static int cec_out_request_current_latency(struct cec_splitter *splitter)
219 {
220 	ktime_t now = ktime_get();
221 	bool error = true;
222 	unsigned int i;
223 
224 	for (i = 0; i < splitter->num_out_ports; i++) {
225 		struct cec_splitter_port *p = splitter->ports[i];
226 		struct cec_adapter *adap = p->adap;
227 
228 		if (!adap->is_configured) {
229 			/* Clear if not configured */
230 			p->out_request_current_latency_seq = 0;
231 			p->out_request_current_latency_ts = ktime_set(0, 0);
232 		} else if (!p->out_request_current_latency_seq) {
233 			/*
234 			 * Keep the old ts if an earlier request is still
235 			 * pending. This ensures that the request will
236 			 * eventually time out based on the timestamp of
237 			 * the first request if the sink is unresponsive.
238 			 */
239 			p->out_request_current_latency_ts = now;
240 		}
241 	}
242 
243 	for (i = 0; i < splitter->num_out_ports; i++) {
244 		struct cec_splitter_port *p = splitter->ports[i];
245 		struct cec_adapter *adap = p->adap;
246 		struct cec_msg msg;
247 
248 		if (!adap->is_configured)
249 			continue;
250 		cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0);
251 		cec_msg_request_current_latency(&msg, true, adap->phys_addr);
252 		if (cec_transmit_msg(adap, &msg, false))
253 			continue;
254 		p->out_request_current_latency_seq = msg.sequence | (1U << 31);
255 		error = false;
256 	}
257 	return error ? -ENODEV : 0;
258 }
259 
260 /*
261  * See if all output ports received the Report Power Status message,
262  * and if so, transmit the result from the input port to the video source.
263  */
cec_out_report_power_status(struct cec_splitter * splitter,struct cec_adapter * input_adap)264 static void cec_out_report_power_status(struct cec_splitter *splitter,
265 					struct cec_adapter *input_adap)
266 {
267 	struct cec_msg reply = {};
268 	/* The target power status of the splitter itself */
269 	u8 splitter_pwr = splitter->is_standby ?
270 		CEC_OP_POWER_STATUS_STANDBY : CEC_OP_POWER_STATUS_ON;
271 	/*
272 	 * The transient power status of the splitter, used if not all
273 	 * output report the target power status.
274 	 */
275 	u8 splitter_transient_pwr = splitter->is_standby ?
276 		CEC_OP_POWER_STATUS_TO_STANDBY : CEC_OP_POWER_STATUS_TO_ON;
277 	u8 reply_pwr = splitter_pwr;
278 	unsigned int i;
279 
280 	for (i = 0; i < splitter->num_out_ports; i++) {
281 		struct cec_splitter_port *p = splitter->ports[i];
282 
283 		/* Skip if no sink was found (HPD was low for more than 5s) */
284 		if (!p->found_sink)
285 			continue;
286 
287 		/* Return if a port is still waiting for a reply */
288 		if (p->out_give_device_power_status_seq)
289 			return;
290 		if (p->power_status != splitter_pwr)
291 			reply_pwr = splitter_transient_pwr;
292 	}
293 
294 	/*
295 	 * All ports that can reply, replied, so clear the sequence
296 	 * and timestamp values.
297 	 */
298 	for (i = 0; i < splitter->num_out_ports; i++) {
299 		struct cec_splitter_port *p = splitter->ports[i];
300 
301 		p->out_give_device_power_status_seq = 0;
302 		p->out_give_device_power_status_ts = ktime_set(0, 0);
303 	}
304 
305 	/* Return if the input port is no longer configured. */
306 	if (!input_adap->is_configured)
307 		return;
308 
309 	/* Reply with the new power status */
310 	cec_msg_init(&reply, input_adap->log_addrs.log_addr[0],
311 		     splitter->give_device_power_status_dest);
312 	cec_msg_report_power_status(&reply, reply_pwr);
313 	cec_transmit_msg(input_adap, &reply, false);
314 }
315 
316 /* Transmit Give Device Power Status to all output ports */
cec_out_give_device_power_status(struct cec_splitter * splitter)317 static int cec_out_give_device_power_status(struct cec_splitter *splitter)
318 {
319 	ktime_t now = ktime_get();
320 	bool error = true;
321 	unsigned int i;
322 
323 	for (i = 0; i < splitter->num_out_ports; i++) {
324 		struct cec_splitter_port *p = splitter->ports[i];
325 		struct cec_adapter *adap = p->adap;
326 
327 		/*
328 		 * Keep the old ts if an earlier request is still
329 		 * pending. This ensures that the request will
330 		 * eventually time out based on the timestamp of
331 		 * the first request if the sink is unresponsive.
332 		 */
333 		if (adap->is_configured && !p->out_give_device_power_status_seq)
334 			p->out_give_device_power_status_ts = now;
335 	}
336 
337 	for (i = 0; i < splitter->num_out_ports; i++) {
338 		struct cec_splitter_port *p = splitter->ports[i];
339 		struct cec_adapter *adap = p->adap;
340 		struct cec_msg msg;
341 
342 		if (!adap->is_configured)
343 			continue;
344 
345 		cec_msg_init(&msg, adap->log_addrs.log_addr[0], 0);
346 		cec_msg_give_device_power_status(&msg, true);
347 		if (cec_transmit_msg(adap, &msg, false))
348 			continue;
349 		p->out_give_device_power_status_seq = msg.sequence | (1U << 31);
350 		error = false;
351 	}
352 	return error ? -ENODEV : 0;
353 }
354 
355 /*
356  * CEC messages received on the HDMI input of the splitter are
357  * forwarded (if relevant) to the HDMI outputs of the splitter.
358  */
cec_splitter_received_input(struct cec_splitter_port * p,struct cec_msg * msg)359 int cec_splitter_received_input(struct cec_splitter_port *p, struct cec_msg *msg)
360 {
361 	if (!cec_msg_status_is_ok(msg))
362 		return 0;
363 
364 	if (msg->len < 2)
365 		return -ENOMSG;
366 
367 	switch (msg->msg[1]) {
368 	case CEC_MSG_DEVICE_VENDOR_ID:
369 	case CEC_MSG_REPORT_POWER_STATUS:
370 	case CEC_MSG_SET_STREAM_PATH:
371 	case CEC_MSG_ROUTING_CHANGE:
372 	case CEC_MSG_REQUEST_ACTIVE_SOURCE:
373 	case CEC_MSG_SYSTEM_AUDIO_MODE_STATUS:
374 		return 0;
375 
376 	case CEC_MSG_STANDBY:
377 		p->splitter->is_standby = true;
378 		cec_out_standby(p->splitter);
379 		return 0;
380 
381 	case CEC_MSG_IMAGE_VIEW_ON:
382 	case CEC_MSG_TEXT_VIEW_ON:
383 		p->splitter->is_standby = false;
384 		cec_out_wakeup(p->splitter, msg->msg[1]);
385 		return 0;
386 
387 	case CEC_MSG_ACTIVE_SOURCE:
388 		cec_out_active_source(p->splitter);
389 		return 0;
390 
391 	case CEC_MSG_SET_SYSTEM_AUDIO_MODE:
392 		cec_out_passthrough(p->splitter, msg);
393 		return 0;
394 
395 	case CEC_MSG_GIVE_DEVICE_POWER_STATUS:
396 		p->splitter->give_device_power_status_dest =
397 			cec_msg_initiator(msg);
398 		if (cec_out_give_device_power_status(p->splitter))
399 			cec_feature_abort_reason(p->adap, msg,
400 						 CEC_OP_ABORT_INCORRECT_MODE);
401 		return 0;
402 
403 	case CEC_MSG_REQUEST_CURRENT_LATENCY: {
404 		u16 pa;
405 
406 		p->splitter->request_current_latency_dest =
407 			cec_msg_initiator(msg);
408 		cec_ops_request_current_latency(msg, &pa);
409 		if (pa == p->adap->phys_addr &&
410 		    cec_out_request_current_latency(p->splitter))
411 			cec_feature_abort_reason(p->adap, msg,
412 						 CEC_OP_ABORT_INCORRECT_MODE);
413 		return 0;
414 	}
415 
416 	default:
417 		return -ENOMSG;
418 	}
419 	return -ENOMSG;
420 }
421 
cec_splitter_nb_transmit_canceled_output(struct cec_splitter_port * p,const struct cec_msg * msg,struct cec_adapter * input_adap)422 void cec_splitter_nb_transmit_canceled_output(struct cec_splitter_port *p,
423 					      const struct cec_msg *msg,
424 					      struct cec_adapter *input_adap)
425 {
426 	struct cec_splitter *splitter = p->splitter;
427 	u32 seq = msg->sequence | (1U << 31);
428 
429 	/*
430 	 * If this is the result of a failed non-blocking transmit, or it is
431 	 * the result of the failed reply to a non-blocking transmit, then
432 	 * check if the original transmit was to get the current power status
433 	 * or latency and, if so, assume that the remove device is for one
434 	 * reason or another unavailable and assume that it is in the same
435 	 * power status as the splitter, or has no video latency.
436 	 */
437 	if ((cec_msg_recv_is_tx_result(msg) && !(msg->tx_status & CEC_TX_STATUS_OK)) ||
438 	    (cec_msg_recv_is_rx_result(msg) && !(msg->rx_status & CEC_RX_STATUS_OK))) {
439 		u8 tx_op = msg->msg[1];
440 
441 		if (msg->len < 2)
442 			return;
443 		if (cec_msg_recv_is_rx_result(msg) &&
444 		    (msg->rx_status & CEC_RX_STATUS_FEATURE_ABORT))
445 			tx_op = msg->msg[2];
446 		switch (tx_op) {
447 		case CEC_MSG_GIVE_DEVICE_POWER_STATUS:
448 			if (p->out_give_device_power_status_seq != seq)
449 				break;
450 			p->out_give_device_power_status_seq = 0;
451 			p->out_give_device_power_status_ts = ktime_set(0, 0);
452 			p->power_status = splitter->is_standby ?
453 					  CEC_OP_POWER_STATUS_STANDBY :
454 					  CEC_OP_POWER_STATUS_ON;
455 			cec_out_report_power_status(splitter, input_adap);
456 			break;
457 		case CEC_MSG_REQUEST_CURRENT_LATENCY:
458 			if (p->out_request_current_latency_seq != seq)
459 				break;
460 			p->video_latency = 1;
461 			p->out_request_current_latency_seq = 0;
462 			p->out_request_current_latency_ts = ktime_set(0, 0);
463 			cec_out_report_current_latency(splitter, input_adap);
464 			break;
465 		}
466 		return;
467 	}
468 
469 	if (cec_msg_recv_is_tx_result(msg)) {
470 		if (p->out_request_current_latency_seq != seq)
471 			return;
472 		p->out_request_current_latency_ts = ns_to_ktime(msg->tx_ts);
473 		return;
474 	}
475 }
476 
477 /*
478  * CEC messages received on an HDMI output of the splitter
479  * are processed here.
480  */
cec_splitter_received_output(struct cec_splitter_port * p,struct cec_msg * msg,struct cec_adapter * input_adap)481 int cec_splitter_received_output(struct cec_splitter_port *p, struct cec_msg *msg,
482 				 struct cec_adapter *input_adap)
483 {
484 	struct cec_adapter *adap = p->adap;
485 	struct cec_splitter *splitter = p->splitter;
486 	u32 seq = msg->sequence | (1U << 31);
487 	struct cec_msg reply = {};
488 	u16 pa;
489 
490 	if (!adap->is_configured || msg->len < 2)
491 		return -ENOMSG;
492 
493 	switch (msg->msg[1]) {
494 	case CEC_MSG_REPORT_POWER_STATUS: {
495 		u8 pwr;
496 
497 		cec_ops_report_power_status(msg, &pwr);
498 		if (pwr > CEC_OP_POWER_STATUS_TO_STANDBY)
499 			pwr = splitter->is_standby ?
500 				CEC_OP_POWER_STATUS_TO_STANDBY :
501 				CEC_OP_POWER_STATUS_TO_ON;
502 		p->power_status = pwr;
503 		if (p->out_give_device_power_status_seq == seq) {
504 			p->out_give_device_power_status_seq = 0;
505 			p->out_give_device_power_status_ts = ktime_set(0, 0);
506 		}
507 		cec_out_report_power_status(splitter, input_adap);
508 		return 0;
509 	}
510 
511 	case CEC_MSG_REPORT_CURRENT_LATENCY: {
512 		u8 video_lat;
513 		u8 low_lat_mode;
514 		u8 audio_out_comp;
515 		u8 audio_out_delay;
516 
517 		cec_ops_report_current_latency(msg, &pa,
518 					       &video_lat, &low_lat_mode,
519 					       &audio_out_comp, &audio_out_delay);
520 		if (!video_lat || video_lat >= 252)
521 			video_lat = 1;
522 		p->video_latency = video_lat;
523 		if (p->out_request_current_latency_seq == seq) {
524 			p->out_request_current_latency_seq = 0;
525 			p->out_request_current_latency_ts = ktime_set(0, 0);
526 		}
527 		cec_out_report_current_latency(splitter, input_adap);
528 		return 0;
529 	}
530 
531 	case CEC_MSG_STANDBY:
532 	case CEC_MSG_ROUTING_CHANGE:
533 	case CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS:
534 		return 0;
535 
536 	case CEC_MSG_ACTIVE_SOURCE:
537 		cec_ops_active_source(msg, &pa);
538 		if (pa == 0)
539 			p->is_active_source = false;
540 		return 0;
541 
542 	case CEC_MSG_REQUEST_ACTIVE_SOURCE:
543 		if (!p->is_active_source)
544 			return 0;
545 		cec_msg_set_reply_to(&reply, msg);
546 		cec_msg_active_source(&reply, adap->phys_addr);
547 		cec_transmit_msg(adap, &reply, false);
548 		return 0;
549 
550 	case CEC_MSG_GIVE_DEVICE_POWER_STATUS:
551 		cec_msg_set_reply_to(&reply, msg);
552 		cec_msg_report_power_status(&reply, splitter->is_standby ?
553 					    CEC_OP_POWER_STATUS_STANDBY :
554 					    CEC_OP_POWER_STATUS_ON);
555 		cec_transmit_msg(adap, &reply, false);
556 		return 0;
557 
558 	case CEC_MSG_SET_STREAM_PATH:
559 		cec_ops_set_stream_path(msg, &pa);
560 		if (pa == adap->phys_addr) {
561 			cec_msg_set_reply_to(&reply, msg);
562 			cec_msg_active_source(&reply, pa);
563 			cec_transmit_msg(adap, &reply, false);
564 		}
565 		return 0;
566 
567 	default:
568 		return -ENOMSG;
569 	}
570 	return -ENOMSG;
571 }
572 
573 /*
574  * Called every second to check for timed out messages and whether there
575  * still is a video sink connected or not.
576  *
577  * Returns true if sinks were lost.
578  */
cec_splitter_poll(struct cec_splitter * splitter,struct cec_adapter * input_adap,bool debug)579 bool cec_splitter_poll(struct cec_splitter *splitter,
580 		       struct cec_adapter *input_adap, bool debug)
581 {
582 	ktime_t now = ktime_get();
583 	u8 pwr = splitter->is_standby ?
584 		 CEC_OP_POWER_STATUS_STANDBY : CEC_OP_POWER_STATUS_ON;
585 	unsigned int max_delay_ms = input_adap->xfer_timeout_ms + 2000;
586 	unsigned int i;
587 	bool res = false;
588 
589 	for (i = 0; i < splitter->num_out_ports; i++) {
590 		struct cec_splitter_port *p = splitter->ports[i];
591 		s64 pwr_delta, lat_delta;
592 		bool pwr_timeout, lat_timeout;
593 
594 		if (!p)
595 			continue;
596 
597 		pwr_delta = ktime_ms_delta(now, p->out_give_device_power_status_ts);
598 		pwr_timeout = p->out_give_device_power_status_seq &&
599 			      pwr_delta >= max_delay_ms;
600 		lat_delta = ktime_ms_delta(now, p->out_request_current_latency_ts);
601 		lat_timeout = p->out_request_current_latency_seq &&
602 			      lat_delta >= max_delay_ms;
603 
604 		/*
605 		 * If the HPD is low for more than 5 seconds, then assume no display
606 		 * is connected.
607 		 */
608 		if (p->found_sink && ktime_to_ns(p->lost_sink_ts) &&
609 		    ktime_ms_delta(now, p->lost_sink_ts) > 5000) {
610 			if (debug)
611 				dev_info(splitter->dev,
612 					 "port %u: HPD low for more than 5s, assume no sink is connected.\n",
613 					 p->port);
614 			p->found_sink = false;
615 			p->lost_sink_ts = ktime_set(0, 0);
616 			res = true;
617 		}
618 
619 		/*
620 		 * If the power status request timed out, then set the port's
621 		 * power status to that of the splitter, ensuring a consistent
622 		 * power state.
623 		 */
624 		if (pwr_timeout) {
625 			mutex_lock(&p->adap->lock);
626 			if (debug)
627 				dev_info(splitter->dev,
628 					 "port %u: give up on power status for seq %u\n",
629 					 p->port,
630 					 p->out_give_device_power_status_seq & ~(1 << 31));
631 			p->power_status = pwr;
632 			p->out_give_device_power_status_seq = 0;
633 			p->out_give_device_power_status_ts = ktime_set(0, 0);
634 			mutex_unlock(&p->adap->lock);
635 			cec_out_report_power_status(splitter, input_adap);
636 		}
637 
638 		/*
639 		 * If the current latency request timed out, then set the port's
640 		 * latency to 1.
641 		 */
642 		if (lat_timeout) {
643 			mutex_lock(&p->adap->lock);
644 			if (debug)
645 				dev_info(splitter->dev,
646 					 "port %u: give up on latency for seq %u\n",
647 					 p->port,
648 					 p->out_request_current_latency_seq & ~(1 << 31));
649 			p->video_latency = 1;
650 			p->out_request_current_latency_seq = 0;
651 			p->out_request_current_latency_ts = ktime_set(0, 0);
652 			mutex_unlock(&p->adap->lock);
653 			cec_out_report_current_latency(splitter, input_adap);
654 		}
655 	}
656 	return res;
657 }
658