• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.h>
28 #include <media/cec-notifier.h>
29 #include <drm/drm_edid.h>
30 
31 struct cec_notifier {
32 	struct mutex lock;
33 	struct list_head head;
34 	struct kref kref;
35 	struct device *dev;
36 	struct cec_adapter *cec_adap;
37 	void (*callback)(struct cec_adapter *adap, u16 pa);
38 
39 	u16 phys_addr;
40 };
41 
42 static LIST_HEAD(cec_notifiers);
43 static DEFINE_MUTEX(cec_notifiers_lock);
44 
cec_notifier_get(struct device * dev)45 struct cec_notifier *cec_notifier_get(struct device *dev)
46 {
47 	struct cec_notifier *n;
48 
49 	mutex_lock(&cec_notifiers_lock);
50 	list_for_each_entry(n, &cec_notifiers, head) {
51 		if (n->dev == dev) {
52 			kref_get(&n->kref);
53 			mutex_unlock(&cec_notifiers_lock);
54 			return n;
55 		}
56 	}
57 	n = kzalloc(sizeof(*n), GFP_KERNEL);
58 	if (!n)
59 		goto unlock;
60 	n->dev = dev;
61 	n->phys_addr = CEC_PHYS_ADDR_INVALID;
62 	mutex_init(&n->lock);
63 	kref_init(&n->kref);
64 	list_add_tail(&n->head, &cec_notifiers);
65 unlock:
66 	mutex_unlock(&cec_notifiers_lock);
67 	return n;
68 }
69 EXPORT_SYMBOL_GPL(cec_notifier_get);
70 
cec_notifier_release(struct kref * kref)71 static void cec_notifier_release(struct kref *kref)
72 {
73 	struct cec_notifier *n =
74 		container_of(kref, struct cec_notifier, kref);
75 
76 	list_del(&n->head);
77 	kfree(n);
78 }
79 
cec_notifier_put(struct cec_notifier * n)80 void cec_notifier_put(struct cec_notifier *n)
81 {
82 	mutex_lock(&cec_notifiers_lock);
83 	kref_put(&n->kref, cec_notifier_release);
84 	mutex_unlock(&cec_notifiers_lock);
85 }
86 EXPORT_SYMBOL_GPL(cec_notifier_put);
87 
cec_notifier_set_phys_addr(struct cec_notifier * n,u16 pa)88 void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa)
89 {
90 	if (n == NULL)
91 		return;
92 
93 	mutex_lock(&n->lock);
94 	n->phys_addr = pa;
95 	if (n->callback)
96 		n->callback(n->cec_adap, n->phys_addr);
97 	mutex_unlock(&n->lock);
98 }
99 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr);
100 
cec_notifier_set_phys_addr_from_edid(struct cec_notifier * n,const struct edid * edid)101 void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n,
102 					  const struct edid *edid)
103 {
104 	u16 pa = CEC_PHYS_ADDR_INVALID;
105 
106 	if (n == NULL)
107 		return;
108 
109 	if (edid && edid->extensions)
110 		pa = cec_get_edid_phys_addr((const u8 *)edid,
111 				EDID_LENGTH * (edid->extensions + 1), NULL);
112 	cec_notifier_set_phys_addr(n, pa);
113 }
114 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid);
115 
cec_notifier_register(struct cec_notifier * n,struct cec_adapter * adap,void (* callback)(struct cec_adapter * adap,u16 pa))116 void cec_notifier_register(struct cec_notifier *n,
117 			   struct cec_adapter *adap,
118 			   void (*callback)(struct cec_adapter *adap, u16 pa))
119 {
120 	kref_get(&n->kref);
121 	mutex_lock(&n->lock);
122 	n->cec_adap = adap;
123 	n->callback = callback;
124 	n->callback(adap, n->phys_addr);
125 	mutex_unlock(&n->lock);
126 }
127 EXPORT_SYMBOL_GPL(cec_notifier_register);
128 
cec_notifier_unregister(struct cec_notifier * n)129 void cec_notifier_unregister(struct cec_notifier *n)
130 {
131 	mutex_lock(&n->lock);
132 	n->callback = NULL;
133 	n->cec_adap->notifier = NULL;
134 	n->cec_adap = NULL;
135 	mutex_unlock(&n->lock);
136 	cec_notifier_put(n);
137 }
138 EXPORT_SYMBOL_GPL(cec_notifier_unregister);
139