diff options
Diffstat (limited to 'drivers/media/cec/cec-notifier.c')
-rw-r--r-- | drivers/media/cec/cec-notifier.c | 112 |
1 files changed, 103 insertions, 9 deletions
diff --git a/drivers/media/cec/cec-notifier.c b/drivers/media/cec/cec-notifier.c index 9598c7778871..52a867bde15f 100644 --- a/drivers/media/cec/cec-notifier.c +++ b/drivers/media/cec/cec-notifier.c @@ -21,8 +21,9 @@ struct cec_notifier { struct mutex lock; struct list_head head; struct kref kref; - struct device *dev; - const char *conn; + 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); @@ -32,14 +33,16 @@ struct cec_notifier { static LIST_HEAD(cec_notifiers); static DEFINE_MUTEX(cec_notifiers_lock); -struct cec_notifier *cec_notifier_get_conn(struct device *dev, const char *conn) +struct cec_notifier * +cec_notifier_get_conn(struct device *hdmi_dev, const char *conn_name) { struct cec_notifier *n; mutex_lock(&cec_notifiers_lock); list_for_each_entry(n, &cec_notifiers, head) { - if (n->dev == dev && - (!conn || !strcmp(n->conn, conn))) { + if (n->hdmi_dev == hdmi_dev && + (!conn_name || + (n->conn_name && !strcmp(n->conn_name, conn_name)))) { kref_get(&n->kref); mutex_unlock(&cec_notifiers_lock); return n; @@ -48,10 +51,17 @@ struct cec_notifier *cec_notifier_get_conn(struct device *dev, const char *conn) n = kzalloc(sizeof(*n), GFP_KERNEL); if (!n) goto unlock; - n->dev = dev; - if (conn) - n->conn = kstrdup(conn, GFP_KERNEL); + n->hdmi_dev = hdmi_dev; + if (conn_name) { + n->conn_name = kstrdup(conn_name, GFP_KERNEL); + if (!n->conn_name) { + kfree(n); + n = NULL; + goto unlock; + } + } n->phys_addr = CEC_PHYS_ADDR_INVALID; + mutex_init(&n->lock); kref_init(&n->kref); list_add_tail(&n->head, &cec_notifiers); @@ -67,7 +77,7 @@ static void cec_notifier_release(struct kref *kref) container_of(kref, struct cec_notifier, kref); list_del(&n->head); - kfree(n->conn); + kfree(n->conn_name); kfree(n); } @@ -79,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) @@ -88,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); @@ -122,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); |