media: cros-ec-cec: Manage an array of ports
authorReka Norman <rekanorman@chromium.org>
Fri, 25 Aug 2023 02:43:55 +0000 (12:43 +1000)
committerHans Verkuil <hverkuil-cisco@xs4all.nl>
Wed, 27 Sep 2023 07:39:54 +0000 (09:39 +0200)
To support multiple CEC ports, change cros_ec_cec to contain an array of
ports, each with their own CEC adapter, etc.

For now, only create a single port and use that port everywhere, so
there is no functional change. Support for multiple ports will be added
in the following patches.

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 8dd95fb385462b31d389c5dd4361ac2fcacf76ae..d76a25ae0cf180dc97a81024158183da544b1c3e 100644 (file)
 
 #define DRV_NAME       "cros-ec-cec"
 
+/* Only one port is supported for now */
+#define CEC_NUM_PORTS  1
+#define CEC_PORT       0
+
 /**
- * struct cros_ec_cec - Driver data for EC CEC
+ * struct cros_ec_cec_port - Driver data for a single EC CEC port
  *
- * @cros_ec: Pointer to EC device
- * @notifier: Notifier info for responding to EC events
+ * @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 {
-       struct cros_ec_device *cros_ec;
-       struct notifier_block notifier;
+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
+ * @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 num_ports;
+       struct cros_ec_cec_port *ports[EC_CEC_MAX_PORTS];
 };
 
 static void handle_cec_message(struct cros_ec_cec *cros_ec_cec)
@@ -43,27 +62,28 @@ 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];
 
        if (len > CEC_MAX_MSG_SIZE)
                len = CEC_MAX_MSG_SIZE;
-       cros_ec_cec->rx_msg.len = len;
-       memcpy(cros_ec_cec->rx_msg.msg, cec_message, len);
+       port->rx_msg.len = len;
+       memcpy(port->rx_msg.msg, cec_message, len);
 
-       cec_received_msg(cros_ec_cec->adap, &cros_ec_cec->rx_msg);
+       cec_received_msg(port->adap, &port->rx_msg);
 }
 
 static void handle_cec_event(struct cros_ec_cec *cros_ec_cec)
 {
        struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
        uint32_t events = cros_ec->event_data.data.cec_events;
+       struct cros_ec_cec_port *port = cros_ec_cec->ports[CEC_PORT];
 
        if (events & EC_MKBP_CEC_SEND_OK)
-               cec_transmit_attempt_done(cros_ec_cec->adap,
-                                         CEC_TX_STATUS_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(cros_ec_cec->adap,
+               cec_transmit_attempt_done(port->adap,
                                          CEC_TX_STATUS_MAX_RETRIES |
                                          CEC_TX_STATUS_NACK);
 }
@@ -93,7 +113,8 @@ static int cros_ec_cec_event(struct notifier_block *nb,
 
 static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
 {
-       struct cros_ec_cec *cros_ec_cec = adap->priv;
+       struct cros_ec_cec_port *port = adap->priv;
+       struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
        struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
        struct ec_params_cec_set params = {
                .cmd = CEC_CMD_LOGICAL_ADDRESS,
@@ -115,7 +136,8 @@ static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
 static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts,
                                u32 signal_free_time, struct cec_msg *cec_msg)
 {
-       struct cros_ec_cec *cros_ec_cec = adap->priv;
+       struct cros_ec_cec_port *port = adap->priv;
+       struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
        struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
        struct ec_params_cec_write params;
        int ret;
@@ -135,7 +157,8 @@ static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts,
 
 static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable)
 {
-       struct cros_ec_cec *cros_ec_cec = adap->priv;
+       struct cros_ec_cec_port *port = adap->priv;
+       struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
        struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
        struct ec_params_cec_set params = {
                .cmd = CEC_CMD_ENABLE,
@@ -260,11 +283,55 @@ static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
 
 #endif
 
+static int cros_ec_cec_init_port(struct device *dev,
+                                struct cros_ec_cec *cros_ec_cec,
+                                int port_num, struct device *hdmi_dev,
+                                const char *conn)
+{
+       struct cros_ec_cec_port *port;
+       int ret;
+
+       port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+       if (!port)
+               return -ENOMEM;
+
+       port->cros_ec_cec = cros_ec_cec;
+       port->port_num = port_num;
+
+       port->adap = cec_allocate_adapter(&cros_ec_cec_ops, port, DRV_NAME,
+                                         CEC_CAP_DEFAULTS |
+                                         CEC_CAP_CONNECTOR_INFO, 1);
+       if (IS_ERR(port->adap))
+               return PTR_ERR(port->adap);
+
+       port->notify = cec_notifier_cec_adap_register(hdmi_dev, conn,
+                                                     port->adap);
+       if (!port->notify) {
+               ret = -ENOMEM;
+               goto out_probe_adapter;
+       }
+
+       ret = cec_register_adapter(port->adap, dev);
+       if (ret < 0)
+               goto out_probe_notify;
+
+       cros_ec_cec->ports[port_num] = port;
+
+       return 0;
+
+out_probe_notify:
+       cec_notifier_cec_adap_unregister(port->notify, port->adap);
+out_probe_adapter:
+       cec_delete_adapter(port->adap);
+       return ret;
+}
+
 static int cros_ec_cec_probe(struct platform_device *pdev)
 {
        struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
        struct cros_ec_device *cros_ec = ec_dev->ec_dev;
        struct cros_ec_cec *cros_ec_cec;
+       struct cros_ec_cec_port *port;
        struct device *hdmi_dev;
        const char *conn = NULL;
        int ret;
@@ -283,18 +350,13 @@ static int cros_ec_cec_probe(struct platform_device *pdev)
 
        device_init_wakeup(&pdev->dev, 1);
 
-       cros_ec_cec->adap = cec_allocate_adapter(&cros_ec_cec_ops, cros_ec_cec,
-                                                DRV_NAME,
-                                                CEC_CAP_DEFAULTS |
-                                                CEC_CAP_CONNECTOR_INFO, 1);
-       if (IS_ERR(cros_ec_cec->adap))
-               return PTR_ERR(cros_ec_cec->adap);
+       cros_ec_cec->num_ports = CEC_NUM_PORTS;
 
-       cros_ec_cec->notify = cec_notifier_cec_adap_register(hdmi_dev, conn,
-                                                            cros_ec_cec->adap);
-       if (!cros_ec_cec->notify) {
-               ret = -ENOMEM;
-               goto out_probe_adapter;
+       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, conn);
+               if (ret)
+                       goto unregister_ports;
        }
 
        /* Get CEC events from the EC. */
@@ -303,20 +365,24 @@ static int cros_ec_cec_probe(struct platform_device *pdev)
                                               &cros_ec_cec->notifier);
        if (ret) {
                dev_err(&pdev->dev, "failed to register notifier\n");
-               goto out_probe_notify;
+               goto unregister_ports;
        }
 
-       ret = cec_register_adapter(cros_ec_cec->adap, &pdev->dev);
-       if (ret < 0)
-               goto out_probe_notify;
-
        return 0;
 
-out_probe_notify:
-       cec_notifier_cec_adap_unregister(cros_ec_cec->notify,
-                                        cros_ec_cec->adap);
-out_probe_adapter:
-       cec_delete_adapter(cros_ec_cec->adap);
+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;
 }
 
@@ -324,6 +390,7 @@ static void cros_ec_cec_remove(struct platform_device *pdev)
 {
        struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev);
        struct device *dev = &pdev->dev;
+       struct cros_ec_cec_port *port;
        int ret;
 
        /*
@@ -337,9 +404,11 @@ static void cros_ec_cec_remove(struct platform_device *pdev)
        if (ret)
                dev_err(dev, "failed to unregister notifier\n");
 
-       cec_notifier_cec_adap_unregister(cros_ec_cec->notify,
-                                        cros_ec_cec->adap);
-       cec_unregister_adapter(cros_ec_cec->adap);
+       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);
+       }
 }
 
 static struct platform_driver cros_ec_cec_driver = {
index ab721cf13a9864c9e4d1fa54218686d9cadcd0e7..cb2ddd10a6137d053af033112cf8725f5d030c94 100644 (file)
@@ -4436,6 +4436,8 @@ struct ec_response_i2c_passthru_protect {
  * These commands are for sending and receiving message via HDMI CEC
  */
 
+#define EC_CEC_MAX_PORTS 16
+
 #define MAX_CEC_MSG_LEN 16
 
 /* CEC message from the AP to be written on the CEC bus */