/** * struct cros_ec_cec_port - Driver data for a single EC CEC port * * @port_num: port number * @adap: CEC adapter * @notify: CEC notifier pointer * @rx_msg: storage for a received message * @cros_ec_cec: pointer to the parent struct
*/ struct cros_ec_cec_port { int port_num; struct cec_adapter *adap; struct cec_notifier *notify; struct cec_msg rx_msg; struct cros_ec_cec *cros_ec_cec;
};
/** * struct cros_ec_cec - Driver data for EC CEC * * @cros_ec: Pointer to EC device * @notifier: Notifier info for responding to EC events * @write_cmd_version: Highest supported version of EC_CMD_CEC_WRITE_MSG. * @num_ports: Number of CEC ports * @ports: Array of ports
*/ struct cros_ec_cec { struct cros_ec_device *cros_ec; struct notifier_block notifier; int write_cmd_version; int num_ports; struct cros_ec_cec_port *ports[EC_CEC_MAX_PORTS];
};
staticvoid cros_ec_cec_received_message(struct cros_ec_cec_port *port,
uint8_t *msg, uint8_t len)
{ if (len > CEC_MAX_MSG_SIZE)
len = CEC_MAX_MSG_SIZE;
staticvoid handle_cec_message(struct cros_ec_cec *cros_ec_cec)
{ struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
uint8_t *cec_message = cros_ec->event_data.data.cec_message; unsignedint len = cros_ec->event_size; struct cros_ec_cec_port *port; /* * There are two ways of receiving CEC messages: * 1. Old EC firmware which only supports one port sends the data in a * cec_message MKBP event. * 2. New EC firmware which supports multiple ports uses * EC_MKBP_CEC_HAVE_DATA to notify that data is ready and * EC_CMD_CEC_READ_MSG to read it. * Check that the EC only has one CEC port, and then we can assume the * message is from port 0.
*/ if (cros_ec_cec->num_ports != 1) {
dev_err(cros_ec->dev, "received cec_message on device with %d ports\n",
cros_ec_cec->num_ports); return;
}
port = cros_ec_cec->ports[0];
if (port_num >= cros_ec_cec->num_ports) {
dev_err(cros_ec->dev, "received CEC event for invalid port %d\n", port_num); return;
}
port = cros_ec_cec->ports[port_num];
if (events & EC_MKBP_CEC_SEND_OK)
cec_transmit_attempt_done(port->adap, CEC_TX_STATUS_OK);
/* FW takes care of all retries, tell core to avoid more retries */ if (events & EC_MKBP_CEC_SEND_FAILED)
cec_transmit_attempt_done(port->adap,
CEC_TX_STATUS_MAX_RETRIES |
CEC_TX_STATUS_NACK);
if (events & EC_MKBP_CEC_HAVE_DATA)
cros_ec_cec_read_message(port);
}
/* * Specify the DRM device name handling the HDMI output and the HDMI connector * corresponding to each CEC port. The order of connectors must match the order * in the EC (first connector is EC port 0, ...), and the number of connectors * must match the number of ports in the EC (which can be queried using the * EC_CMD_CEC_PORT_COUNT host command).
*/
for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) { conststruct cec_dmi_match *m = &cec_dmi_match_table[i];
if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
dmi_match(DMI_PRODUCT_NAME, m->product_name)) { struct device *d;
/* Find the device, bail out if not yet registered */
d = bus_find_device_by_name(&pci_bus_type, NULL,
m->devname); if (!d) return ERR_PTR(-EPROBE_DEFER);
put_device(d);
*conns = m->conns; return d;
}
}
/* Hardware support must be added in the cec_dmi_match_table */
dev_warn(dev, "CEC notifier not configured for this hardware\n");
staticint cros_ec_cec_get_num_ports(struct cros_ec_cec *cros_ec_cec)
{ struct ec_response_cec_port_count response; int ret;
ret = cros_ec_cmd(cros_ec_cec->cros_ec, 0, EC_CMD_CEC_PORT_COUNT, NULL,
0, &response, sizeof(response)); if (ret < 0) { /* * Old EC firmware only supports one port and does not support * the port count command, so fall back to assuming one port.
*/
cros_ec_cec->num_ports = 1; return 0;
}
ret = cros_ec_cec_get_num_ports(cros_ec_cec); if (ret) return ret;
ret = cros_ec_cec_get_write_cmd_version(cros_ec_cec); if (ret) return ret;
for (int i = 0; i < cros_ec_cec->num_ports; i++) {
ret = cros_ec_cec_init_port(&pdev->dev, cros_ec_cec, i,
hdmi_dev, conns); if (ret) goto unregister_ports;
}
/* Get CEC events from the EC. */
cros_ec_cec->notifier.notifier_call = cros_ec_cec_event;
ret = blocking_notifier_chain_register(&cros_ec->event_notifier,
&cros_ec_cec->notifier); if (ret) {
dev_err(&pdev->dev, "failed to register notifier\n"); goto unregister_ports;
}
return 0;
unregister_ports: /* * Unregister any adapters which have been registered. We don't add the * port to the array until the adapter has been registered successfully, * so any non-NULL ports must have been registered.
*/ for (int i = 0; i < cros_ec_cec->num_ports; i++) {
port = cros_ec_cec->ports[i]; if (!port) break;
cec_notifier_cec_adap_unregister(port->notify, port->adap);
cec_unregister_adapter(port->adap);
} return ret;
}
/* * blocking_notifier_chain_unregister() only fails if the notifier isn't * in the list. We know it was added to it by .probe(), so there should * be no need for error checking. Be cautious and still check.
*/
ret = blocking_notifier_chain_unregister(
&cros_ec_cec->cros_ec->event_notifier,
&cros_ec_cec->notifier); if (ret)
dev_err(dev, "failed to unregister notifier\n");
for (int i = 0; i < cros_ec_cec->num_ports; i++) {
port = cros_ec_cec->ports[i];
cec_notifier_cec_adap_unregister(port->notify, port->adap);
cec_unregister_adapter(port->adap);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.