1 // SPDX-License-Identifier: GPL-2.0-only
4 * Copyright 2021-2024 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
9 #include "cec-splitter.h"
12 * Helper function to reply to a received message with a Feature Abort
15 static int cec_feature_abort_reason(struct cec_adapter
*adap
,
16 struct cec_msg
*msg
, u8 reason
)
18 struct cec_msg tx_msg
= { };
21 * Don't reply with CEC_MSG_FEATURE_ABORT to a CEC_MSG_FEATURE_ABORT
24 if (msg
->msg
[1] == CEC_MSG_FEATURE_ABORT
)
26 /* Don't Feature Abort messages from 'Unregistered' */
27 if (cec_msg_initiator(msg
) == CEC_LOG_ADDR_UNREGISTERED
)
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);
34 /* Transmit an Active Source message from this output port to a sink */
35 static void cec_port_out_active_source(struct cec_splitter_port
*p
)
37 struct cec_adapter
*adap
= p
->adap
;
40 if (!adap
->is_configured
)
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);
48 /* Transmit Active Source messages from all output ports to the sinks */
49 static void cec_out_active_source(struct cec_splitter
*splitter
)
53 for (i
= 0; i
< splitter
->num_out_ports
; i
++)
54 cec_port_out_active_source(splitter
->ports
[i
]);
57 /* Transmit a Standby message from this output port to a sink */
58 static void cec_port_out_standby(struct cec_splitter_port
*p
)
60 struct cec_adapter
*adap
= p
->adap
;
63 if (!adap
->is_configured
)
65 cec_msg_init(&msg
, adap
->log_addrs
.log_addr
[0], 0);
66 cec_msg_standby(&msg
);
67 cec_transmit_msg(adap
, &msg
, false);
70 /* Transmit Standby messages from all output ports to the sinks */
71 static void cec_out_standby(struct cec_splitter
*splitter
)
75 for (i
= 0; i
< splitter
->num_out_ports
; i
++)
76 cec_port_out_standby(splitter
->ports
[i
]);
79 /* Transmit an Image/Text View On message from this output port to a sink */
80 static void cec_port_out_wakeup(struct cec_splitter_port
*p
, u8 opcode
)
82 struct cec_adapter
*adap
= p
->adap
;
83 u8 la
= adap
->log_addrs
.log_addr
[0];
86 if (la
== CEC_LOG_ADDR_INVALID
)
87 la
= CEC_LOG_ADDR_UNREGISTERED
;
88 cec_msg_init(&msg
, la
, 0);
91 cec_transmit_msg(adap
, &msg
, false);
94 /* Transmit Image/Text View On messages from all output ports to the sinks */
95 static void cec_out_wakeup(struct cec_splitter
*splitter
, u8 opcode
)
99 for (i
= 0; i
< splitter
->num_out_ports
; i
++)
100 cec_port_out_wakeup(splitter
->ports
[i
], opcode
);
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.
108 void cec_splitter_unconfigured_output(struct cec_splitter_port
*p
)
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
;
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);
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.
126 void cec_splitter_configured_output(struct cec_splitter_port
*p
)
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
;
132 if (p
->splitter
->is_standby
) {
134 * Some sinks only obey Standby if it comes from the
137 cec_port_out_active_source(p
);
138 cec_port_out_standby(p
);
140 cec_port_out_wakeup(p
, CEC_MSG_IMAGE_VIEW_ON
);
144 /* Pass the in_msg on to all output ports */
145 static void cec_out_passthrough(struct cec_splitter
*splitter
,
146 const struct cec_msg
*in_msg
)
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
;
155 if (!adap
->is_configured
)
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);
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.
168 static void cec_out_report_current_latency(struct cec_splitter
*splitter
,
169 struct cec_adapter
*input_adap
)
171 struct cec_msg reply
= {};
172 unsigned int reply_lat
= 0;
173 unsigned int cnt
= 0;
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
;
180 /* Skip unconfigured ports */
181 if (!adap
->is_configured
)
183 /* Return if a port is still waiting for a reply */
184 if (p
->out_request_current_latency_seq
)
186 reply_lat
+= p
->video_latency
- 1;
191 * All ports that can reply, replied, so clear the sequence
192 * and timestamp values.
194 for (i
= 0; i
< splitter
->num_out_ports
; i
++) {
195 struct cec_splitter_port
*p
= splitter
->ports
[i
];
197 p
->out_request_current_latency_seq
= 0;
198 p
->out_request_current_latency_ts
= ktime_set(0, 0);
202 * Return if there were no replies or the input port is no longer
205 if (!cnt
|| !input_adap
->is_configured
)
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
,
214 cec_transmit_msg(input_adap
, &reply
, false);
217 /* Transmit Request Current Latency to all output ports */
218 static int cec_out_request_current_latency(struct cec_splitter
*splitter
)
220 ktime_t now
= ktime_get();
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
;
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
) {
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.
239 p
->out_request_current_latency_ts
= now
;
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
;
248 if (!adap
->is_configured
)
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))
254 p
->out_request_current_latency_seq
= msg
.sequence
| (1U << 31);
257 return error
? -ENODEV
: 0;
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.
264 static void cec_out_report_power_status(struct cec_splitter
*splitter
,
265 struct cec_adapter
*input_adap
)
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
;
272 * The transient power status of the splitter, used if not all
273 * output report the target power status.
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
;
280 for (i
= 0; i
< splitter
->num_out_ports
; i
++) {
281 struct cec_splitter_port
*p
= splitter
->ports
[i
];
283 /* Skip if no sink was found (HPD was low for more than 5s) */
287 /* Return if a port is still waiting for a reply */
288 if (p
->out_give_device_power_status_seq
)
290 if (p
->power_status
!= splitter_pwr
)
291 reply_pwr
= splitter_transient_pwr
;
295 * All ports that can reply, replied, so clear the sequence
296 * and timestamp values.
298 for (i
= 0; i
< splitter
->num_out_ports
; i
++) {
299 struct cec_splitter_port
*p
= splitter
->ports
[i
];
301 p
->out_give_device_power_status_seq
= 0;
302 p
->out_give_device_power_status_ts
= ktime_set(0, 0);
305 /* Return if the input port is no longer configured. */
306 if (!input_adap
->is_configured
)
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);
316 /* Transmit Give Device Power Status to all output ports */
317 static int cec_out_give_device_power_status(struct cec_splitter
*splitter
)
319 ktime_t now
= ktime_get();
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
;
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.
333 if (adap
->is_configured
&& !p
->out_give_device_power_status_seq
)
334 p
->out_give_device_power_status_ts
= now
;
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
;
342 if (!adap
->is_configured
)
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))
349 p
->out_give_device_power_status_seq
= msg
.sequence
| (1U << 31);
352 return error
? -ENODEV
: 0;
356 * CEC messages received on the HDMI input of the splitter are
357 * forwarded (if relevant) to the HDMI outputs of the splitter.
359 int cec_splitter_received_input(struct cec_splitter_port
*p
, struct cec_msg
*msg
)
361 if (!cec_msg_status_is_ok(msg
))
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
:
376 case CEC_MSG_STANDBY
:
377 p
->splitter
->is_standby
= true;
378 cec_out_standby(p
->splitter
);
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]);
387 case CEC_MSG_ACTIVE_SOURCE
:
388 cec_out_active_source(p
->splitter
);
391 case CEC_MSG_SET_SYSTEM_AUDIO_MODE
:
392 cec_out_passthrough(p
->splitter
, msg
);
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
);
403 case CEC_MSG_REQUEST_CURRENT_LATENCY
: {
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
);
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
)
426 struct cec_splitter
*splitter
= p
->splitter
;
427 u32 seq
= msg
->sequence
| (1U << 31);
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.
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];
443 if (cec_msg_recv_is_rx_result(msg
) &&
444 (msg
->rx_status
& CEC_RX_STATUS_FEATURE_ABORT
))
447 case CEC_MSG_GIVE_DEVICE_POWER_STATUS
:
448 if (p
->out_give_device_power_status_seq
!= seq
)
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
);
457 case CEC_MSG_REQUEST_CURRENT_LATENCY
:
458 if (p
->out_request_current_latency_seq
!= seq
)
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
);
469 if (cec_msg_recv_is_tx_result(msg
)) {
470 if (p
->out_request_current_latency_seq
!= seq
)
472 p
->out_request_current_latency_ts
= ns_to_ktime(msg
->tx_ts
);
478 * CEC messages received on an HDMI output of the splitter
479 * are processed here.
481 int cec_splitter_received_output(struct cec_splitter_port
*p
, struct cec_msg
*msg
,
482 struct cec_adapter
*input_adap
)
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
= {};
490 if (!adap
->is_configured
|| msg
->len
< 2)
493 switch (msg
->msg
[1]) {
494 case CEC_MSG_REPORT_POWER_STATUS
: {
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);
507 cec_out_report_power_status(splitter
, input_adap
);
511 case CEC_MSG_REPORT_CURRENT_LATENCY
: {
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)
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);
527 cec_out_report_current_latency(splitter
, input_adap
);
531 case CEC_MSG_STANDBY
:
532 case CEC_MSG_ROUTING_CHANGE
:
533 case CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS
:
536 case CEC_MSG_ACTIVE_SOURCE
:
537 cec_ops_active_source(msg
, &pa
);
539 p
->is_active_source
= false;
542 case CEC_MSG_REQUEST_ACTIVE_SOURCE
:
543 if (!p
->is_active_source
)
545 cec_msg_set_reply_to(&reply
, msg
);
546 cec_msg_active_source(&reply
, adap
->phys_addr
);
547 cec_transmit_msg(adap
, &reply
, false);
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);
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);
574 * Called every second to check for timed out messages and whether there
575 * still is a video sink connected or not.
577 * Returns true if sinks were lost.
579 bool cec_splitter_poll(struct cec_splitter
*splitter
,
580 struct cec_adapter
*input_adap
, bool debug
)
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;
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
;
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
;
605 * If the HPD is low for more than 5 seconds, then assume no display
608 if (p
->found_sink
&& ktime_to_ns(p
->lost_sink_ts
) &&
609 ktime_ms_delta(now
, p
->lost_sink_ts
) > 5000) {
611 dev_info(splitter
->dev
,
612 "port %u: HPD low for more than 5s, assume no sink is connected.\n",
614 p
->found_sink
= false;
615 p
->lost_sink_ts
= ktime_set(0, 0);
620 * If the power status request timed out, then set the port's
621 * power status to that of the splitter, ensuring a consistent
625 mutex_lock(&p
->adap
->lock
);
627 dev_info(splitter
->dev
,
628 "port %u: give up on power status for seq %u\n",
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
);
639 * If the current latency request timed out, then set the port's
643 mutex_lock(&p
->adap
->lock
);
645 dev_info(splitter
->dev
,
646 "port %u: give up on latency for seq %u\n",
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
);