media: cros-ec-cec: Support receiving messages from multiple ports
authorReka Norman <rekanorman@chromium.org>
Fri, 25 Aug 2023 02:43:59 +0000 (12:43 +1000)
committerHans Verkuil <hverkuil-cisco@xs4all.nl>
Wed, 27 Sep 2023 07:39:54 +0000 (09:39 +0200)
Currently, received messages are sent from the EC in the cec_message
MKBP event. Since the size of ec_response_get_next_data_v1 is 16 bytes,
which is also the maximum size of a CEC message, there is no space to
add a port parameter. Increasing the size of
ec_response_get_next_data_v1 is an option, but this would increase
EC-kernel traffic for all MKBP event types.

Instead, use an event to notify that data is ready, and add a new read
command to read the data.

For backwards compatibility with old EC firmware, continue to handle
cec_message events as well.

Signed-off-by: Reka Norman <rekanorman@chromium.org>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
drivers/media/cec/platform/cros-ec/cros-ec-cec.c
include/linux/platform_data/cros_ec_commands.h

index 18f78b7e034a132dd72fd4dab003c1e430bf5496..6989e63c05beab15b1795eb717bd3ac0537516e7 100644 (file)
@@ -59,19 +59,63 @@ struct cros_ec_cec {
        struct cros_ec_cec_port *ports[EC_CEC_MAX_PORTS];
 };
 
+static void 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;
+
+       port->rx_msg.len = len;
+       memcpy(port->rx_msg.msg, msg, len);
+
+       cec_received_msg(port->adap, &port->rx_msg);
+}
+
 static void 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;
        unsigned int len = cros_ec->event_size;
-       struct cros_ec_cec_port *port = cros_ec_cec->ports[CEC_PORT];
+       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 (len > CEC_MAX_MSG_SIZE)
-               len = CEC_MAX_MSG_SIZE;
-       port->rx_msg.len = len;
-       memcpy(port->rx_msg.msg, cec_message, len);
+       cros_ec_cec_received_message(port, cec_message, len);
+}
 
-       cec_received_msg(port->adap, &port->rx_msg);
+static void cros_ec_cec_read_message(struct cros_ec_cec_port *port)
+{
+       struct cros_ec_device *cros_ec = port->cros_ec_cec->cros_ec;
+       struct ec_params_cec_read params = {
+               .port = port->port_num,
+       };
+       struct ec_response_cec_read response;
+       int ret;
+
+       ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_READ_MSG, &params,
+                         sizeof(params), &response, sizeof(response));
+       if (ret < 0) {
+               dev_err(cros_ec->dev,
+                       "error reading CEC message on EC: %d\n", ret);
+               return;
+       }
+
+       cros_ec_cec_received_message(port, response.msg, response.msg_len);
 }
 
 static void handle_cec_event(struct cros_ec_cec *cros_ec_cec)
@@ -97,6 +141,9 @@ static void handle_cec_event(struct cros_ec_cec *cros_ec_cec)
                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);
 }
 
 static int cros_ec_cec_event(struct notifier_block *nb,
index b7e8573a8a495b5628f20d0f771b0044183c30b5..ad61c7ff0b28506e8377d98616514256f331ac0a 100644 (file)
@@ -4473,6 +4473,27 @@ struct ec_params_cec_write_v1 {
        uint8_t msg[MAX_CEC_MSG_LEN];
 } __ec_align1;
 
+/* CEC message read from a CEC bus reported back to the AP */
+#define EC_CMD_CEC_READ_MSG 0x00B9
+
+/**
+ * struct ec_params_cec_read - Read a message from the CEC bus
+ * @port: CEC port to read a message on
+ */
+struct ec_params_cec_read {
+       uint8_t port;
+} __ec_align1;
+
+/**
+ * struct ec_response_cec_read - Message read from the CEC bus
+ * @msg_len: length of msg in bytes
+ * @msg: message content read from the CEC bus
+ */
+struct ec_response_cec_read {
+       uint8_t msg_len;
+       uint8_t msg[MAX_CEC_MSG_LEN];
+} __ec_align1;
+
 /* Set various CEC parameters */
 #define EC_CMD_CEC_SET 0x00BA
 
@@ -4529,6 +4550,8 @@ enum mkbp_cec_event {
        EC_MKBP_CEC_SEND_OK                     = BIT(0),
        /* Outgoing message was not acknowledged */
        EC_MKBP_CEC_SEND_FAILED                 = BIT(1),
+       /* Incoming message can be read out by AP */
+       EC_MKBP_CEC_HAVE_DATA                   = BIT(2),
 };
 
 /*****************************************************************************/