]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/media/cec/cec-notifier.c
[media] cec: Kconfig cleanup
[karo-tx-linux.git] / drivers / media / cec / cec-notifier.c
1 /*
2  * cec-notifier.c - notify CEC drivers of physical address changes
3  *
4  * Copyright 2016 Russell King <rmk+kernel@arm.linux.org.uk>
5  * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
6  *
7  * This program is free software; you may redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; version 2 of the License.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
13  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
14  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
15  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
16  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
17  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18  * SOFTWARE.
19  */
20
21 #include <linux/export.h>
22 #include <linux/string.h>
23 #include <linux/slab.h>
24 #include <linux/list.h>
25 #include <linux/kref.h>
26
27 #include <media/cec-notifier.h>
28 #include <drm/drm_edid.h>
29
30 struct cec_notifier {
31         struct mutex lock;
32         struct list_head head;
33         struct kref kref;
34         struct device *dev;
35         struct cec_adapter *cec_adap;
36         void (*callback)(struct cec_adapter *adap, u16 pa);
37
38         u16 phys_addr;
39 };
40
41 static LIST_HEAD(cec_notifiers);
42 static DEFINE_MUTEX(cec_notifiers_lock);
43
44 struct cec_notifier *cec_notifier_get(struct device *dev)
45 {
46         struct cec_notifier *n;
47
48         mutex_lock(&cec_notifiers_lock);
49         list_for_each_entry(n, &cec_notifiers, head) {
50                 if (n->dev == dev) {
51                         kref_get(&n->kref);
52                         mutex_unlock(&cec_notifiers_lock);
53                         return n;
54                 }
55         }
56         n = kzalloc(sizeof(*n), GFP_KERNEL);
57         if (!n)
58                 goto unlock;
59         n->dev = dev;
60         n->phys_addr = CEC_PHYS_ADDR_INVALID;
61         mutex_init(&n->lock);
62         kref_init(&n->kref);
63         list_add_tail(&n->head, &cec_notifiers);
64 unlock:
65         mutex_unlock(&cec_notifiers_lock);
66         return n;
67 }
68 EXPORT_SYMBOL_GPL(cec_notifier_get);
69
70 static void cec_notifier_release(struct kref *kref)
71 {
72         struct cec_notifier *n =
73                 container_of(kref, struct cec_notifier, kref);
74
75         list_del(&n->head);
76         kfree(n);
77 }
78
79 void cec_notifier_put(struct cec_notifier *n)
80 {
81         mutex_lock(&cec_notifiers_lock);
82         kref_put(&n->kref, cec_notifier_release);
83         mutex_unlock(&cec_notifiers_lock);
84 }
85 EXPORT_SYMBOL_GPL(cec_notifier_put);
86
87 void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa)
88 {
89         mutex_lock(&n->lock);
90         n->phys_addr = pa;
91         if (n->callback)
92                 n->callback(n->cec_adap, n->phys_addr);
93         mutex_unlock(&n->lock);
94 }
95 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr);
96
97 void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n,
98                                           const struct edid *edid)
99 {
100         u16 pa = CEC_PHYS_ADDR_INVALID;
101
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);
106 }
107 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid);
108
109 void cec_notifier_register(struct cec_notifier *n,
110                            struct cec_adapter *adap,
111                            void (*callback)(struct cec_adapter *adap, u16 pa))
112 {
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);
119 }
120 EXPORT_SYMBOL_GPL(cec_notifier_register);
121
122 void cec_notifier_unregister(struct cec_notifier *n)
123 {
124         mutex_lock(&n->lock);
125         n->callback = NULL;
126         mutex_unlock(&n->lock);
127         cec_notifier_put(n);
128 }
129 EXPORT_SYMBOL_GPL(cec_notifier_unregister);