media: cec-notifier: add new notifier functions
authorHans Verkuil <hverkuil-cisco@xs4all.nl>
Thu, 20 Jun 2019 10:10:00 +0000 (06:10 -0400)
committerMauro Carvalho Chehab <mchehab+samsung@kernel.org>
Thu, 27 Jun 2019 11:19:43 +0000 (07:19 -0400)
In order to support multiple CEC devices for an HDMI connector,
and to support cec_connector_info, drivers should use either a
cec_notifier_conn_(un)register pair of functions (HDMI drivers)
or a cec_notifier_cec_adap_(un)register pair (CEC adapter drivers).

This replaces cec_notifier_get_conn/cec_notifier_put.

For CEC adapters it is also no longer needed to call cec_notifier_register,
cec_register_cec_notifier and cec_notifier_unregister. This is now
all handled internally by the new functions.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
drivers/media/cec/cec-notifier.c
include/media/cec-notifier.h

index f72b19c..52a867b 100644 (file)
@@ -22,6 +22,7 @@ struct cec_notifier {
        struct list_head head;
        struct kref kref;
        struct device *hdmi_dev;
+       struct cec_connector_info conn_info;
        const char *conn_name;
        struct cec_adapter *cec_adap;
        void (*callback)(struct cec_adapter *adap, u16 pa);
@@ -88,6 +89,84 @@ void cec_notifier_put(struct cec_notifier *n)
 }
 EXPORT_SYMBOL_GPL(cec_notifier_put);
 
+struct cec_notifier *
+cec_notifier_conn_register(struct device *hdmi_dev, const char *conn_name,
+                          const struct cec_connector_info *conn_info)
+{
+       struct cec_notifier *n = cec_notifier_get_conn(hdmi_dev, conn_name);
+
+       if (!n)
+               return n;
+
+       mutex_lock(&n->lock);
+       n->phys_addr = CEC_PHYS_ADDR_INVALID;
+       if (conn_info)
+               n->conn_info = *conn_info;
+       else
+               memset(&n->conn_info, 0, sizeof(n->conn_info));
+       if (n->cec_adap) {
+               cec_phys_addr_invalidate(n->cec_adap);
+               cec_s_conn_info(n->cec_adap, conn_info);
+       }
+       mutex_unlock(&n->lock);
+       return n;
+}
+EXPORT_SYMBOL_GPL(cec_notifier_conn_register);
+
+void cec_notifier_conn_unregister(struct cec_notifier *n)
+{
+       if (!n)
+               return;
+
+       mutex_lock(&n->lock);
+       memset(&n->conn_info, 0, sizeof(n->conn_info));
+       n->phys_addr = CEC_PHYS_ADDR_INVALID;
+       if (n->cec_adap) {
+               cec_phys_addr_invalidate(n->cec_adap);
+               cec_s_conn_info(n->cec_adap, NULL);
+       }
+       mutex_unlock(&n->lock);
+       cec_notifier_put(n);
+}
+EXPORT_SYMBOL_GPL(cec_notifier_conn_unregister);
+
+struct cec_notifier *
+cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *conn_name,
+                              struct cec_adapter *adap)
+{
+       struct cec_notifier *n;
+
+       if (WARN_ON(!adap))
+               return NULL;
+
+       n = cec_notifier_get_conn(hdmi_dev, conn_name);
+       if (!n)
+               return n;
+
+       mutex_lock(&n->lock);
+       n->cec_adap = adap;
+       adap->conn_info = n->conn_info;
+       adap->notifier = n;
+       cec_s_phys_addr(adap, n->phys_addr, false);
+       mutex_unlock(&n->lock);
+       return n;
+}
+EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_register);
+
+void cec_notifier_cec_adap_unregister(struct cec_notifier *n)
+{
+       if (!n)
+               return;
+
+       mutex_lock(&n->lock);
+       n->cec_adap->notifier = NULL;
+       n->cec_adap = NULL;
+       n->callback = NULL;
+       mutex_unlock(&n->lock);
+       cec_notifier_put(n);
+}
+EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_unregister);
+
 void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa)
 {
        if (n == NULL)
@@ -97,6 +176,8 @@ void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa)
        n->phys_addr = pa;
        if (n->callback)
                n->callback(n->cec_adap, n->phys_addr);
+       else if (n->cec_adap)
+               cec_s_phys_addr(n->cec_adap, n->phys_addr, false);
        mutex_unlock(&n->lock);
 }
 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr);
@@ -131,6 +212,10 @@ EXPORT_SYMBOL_GPL(cec_notifier_register);
 
 void cec_notifier_unregister(struct cec_notifier *n)
 {
+       /* Do nothing unless cec_notifier_register was called first */
+       if (!n->callback)
+               return;
+
        mutex_lock(&n->lock);
        n->callback = NULL;
        mutex_unlock(&n->lock);
index 0e3bd34..f161f8a 100644 (file)
@@ -42,6 +42,60 @@ struct cec_notifier *cec_notifier_get_conn(struct device *dev,
  */
 void cec_notifier_put(struct cec_notifier *n);
 
+/**
+ * cec_notifier_conn_register - find or create a new cec_notifier for the given
+ * HDMI device and connector tuple.
+ * @hdmi_dev: HDMI device that sends the events.
+ * @conn_name: the connector name from which the event occurs. May be NULL
+ * if there is always only one HDMI connector created by the HDMI device.
+ * @conn_info: the connector info from which the event occurs (may be NULL)
+ *
+ * If a notifier for device @dev and connector @conn_name already exists, then
+ * increase the refcount and return that notifier.
+ *
+ * If it doesn't exist, then allocate a new notifier struct and return a
+ * pointer to that new struct.
+ *
+ * Return NULL if the memory could not be allocated.
+ */
+struct cec_notifier *
+cec_notifier_conn_register(struct device *hdmi_dev, const char *conn_name,
+                          const struct cec_connector_info *conn_info);
+
+/**
+ * cec_notifier_conn_unregister - decrease refcount and delete when the
+ * refcount reaches 0.
+ * @n: notifier. If NULL, then this function does nothing.
+ */
+void cec_notifier_conn_unregister(struct cec_notifier *n);
+
+/**
+ * cec_notifier_cec_adap_register - find or create a new cec_notifier for the
+ * given device.
+ * @hdmi_dev: HDMI device that sends the events.
+ * @conn_name: the connector name from which the event occurs. May be NULL
+ * if there is always only one HDMI connector created by the HDMI device.
+ * @adap: the cec adapter that registered this notifier.
+ *
+ * If a notifier for device @dev and connector @conn_name already exists, then
+ * increase the refcount and return that notifier.
+ *
+ * If it doesn't exist, then allocate a new notifier struct and return a
+ * pointer to that new struct.
+ *
+ * Return NULL if the memory could not be allocated.
+ */
+struct cec_notifier *
+cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *conn_name,
+                              struct cec_adapter *adap);
+
+/**
+ * cec_notifier_cec_adap_unregister - decrease refcount and delete when the
+ * refcount reaches 0.
+ * @n: notifier. If NULL, then this function does nothing.
+ */
+void cec_notifier_cec_adap_unregister(struct cec_notifier *n);
+
 /**
  * cec_notifier_set_phys_addr - set a new physical address.
  * @n: the CEC notifier
@@ -86,6 +140,30 @@ static inline void cec_notifier_put(struct cec_notifier *n)
 {
 }
 
+static inline struct cec_notifier *
+cec_notifier_conn_register(struct device *hdmi_dev, const char *conn_name,
+                          const struct cec_connector_info *conn_info)
+{
+       /* A non-NULL pointer is expected on success */
+       return (struct cec_notifier *)0xdeadfeed;
+}
+
+static inline void cec_notifier_conn_unregister(struct cec_notifier *n)
+{
+}
+
+static inline struct cec_notifier *
+cec_notifier_cec_adap_register(struct device *hdmi_dev, const char *conn_name,
+                              struct cec_adapter *adap)
+{
+       /* A non-NULL pointer is expected on success */
+       return (struct cec_notifier *)0xdeadfeed;
+}
+
+static inline void cec_notifier_cec_adap_unregister(struct cec_notifier *n)
+{
+}
+
 static inline void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa)
 {
 }