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