Linux 4.19.133
[linux/fpc-iii.git] / drivers / media / cec / cec-notifier.c
blob2424680f71c3db3abb62065251928a567237fa12
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * cec-notifier.c - notify CEC drivers of physical address changes
5 * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk>
6 * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
7 */
9 #include <linux/export.h>
10 #include <linux/string.h>
11 #include <linux/slab.h>
12 #include <linux/list.h>
13 #include <linux/kref.h>
15 #include <media/cec.h>
16 #include <media/cec-notifier.h>
17 #include <drm/drm_edid.h>
19 struct cec_notifier {
20 struct mutex lock;
21 struct list_head head;
22 struct kref kref;
23 struct device *dev;
24 const char *conn;
25 struct cec_adapter *cec_adap;
26 void (*callback)(struct cec_adapter *adap, u16 pa);
28 u16 phys_addr;
31 static LIST_HEAD(cec_notifiers);
32 static DEFINE_MUTEX(cec_notifiers_lock);
34 struct cec_notifier *cec_notifier_get_conn(struct device *dev, const char *conn)
36 struct cec_notifier *n;
38 mutex_lock(&cec_notifiers_lock);
39 list_for_each_entry(n, &cec_notifiers, head) {
40 if (n->dev == dev &&
41 (!conn || !strcmp(n->conn, conn))) {
42 kref_get(&n->kref);
43 mutex_unlock(&cec_notifiers_lock);
44 return n;
47 n = kzalloc(sizeof(*n), GFP_KERNEL);
48 if (!n)
49 goto unlock;
50 n->dev = dev;
51 if (conn)
52 n->conn = kstrdup(conn, GFP_KERNEL);
53 n->phys_addr = CEC_PHYS_ADDR_INVALID;
54 mutex_init(&n->lock);
55 kref_init(&n->kref);
56 list_add_tail(&n->head, &cec_notifiers);
57 unlock:
58 mutex_unlock(&cec_notifiers_lock);
59 return n;
61 EXPORT_SYMBOL_GPL(cec_notifier_get_conn);
63 static void cec_notifier_release(struct kref *kref)
65 struct cec_notifier *n =
66 container_of(kref, struct cec_notifier, kref);
68 list_del(&n->head);
69 kfree(n->conn);
70 kfree(n);
73 void cec_notifier_put(struct cec_notifier *n)
75 mutex_lock(&cec_notifiers_lock);
76 kref_put(&n->kref, cec_notifier_release);
77 mutex_unlock(&cec_notifiers_lock);
79 EXPORT_SYMBOL_GPL(cec_notifier_put);
81 void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa)
83 if (n == NULL)
84 return;
86 mutex_lock(&n->lock);
87 n->phys_addr = pa;
88 if (n->callback)
89 n->callback(n->cec_adap, n->phys_addr);
90 mutex_unlock(&n->lock);
92 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr);
94 void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n,
95 const struct edid *edid)
97 u16 pa = CEC_PHYS_ADDR_INVALID;
99 if (n == NULL)
100 return;
102 if (edid && edid->extensions)
103 pa = cec_get_edid_phys_addr((const u8 *)edid,
104 EDID_LENGTH * (edid->extensions + 1), NULL);
105 cec_notifier_set_phys_addr(n, pa);
107 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid);
109 void cec_notifier_register(struct cec_notifier *n,
110 struct cec_adapter *adap,
111 void (*callback)(struct cec_adapter *adap, u16 pa))
113 kref_get(&n->kref);
114 mutex_lock(&n->lock);
115 n->cec_adap = adap;
116 n->callback = callback;
117 n->callback(adap, n->phys_addr);
118 mutex_unlock(&n->lock);
120 EXPORT_SYMBOL_GPL(cec_notifier_register);
122 void cec_notifier_unregister(struct cec_notifier *n)
124 mutex_lock(&n->lock);
125 n->callback = NULL;
126 n->cec_adap->notifier = NULL;
127 n->cec_adap = NULL;
128 mutex_unlock(&n->lock);
129 cec_notifier_put(n);
131 EXPORT_SYMBOL_GPL(cec_notifier_unregister);