1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * cec-notifier.c - notify CEC drivers of physical address changes
4  *
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  */
8 
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>
14 
15 #include <media/cec.h>
16 #include <media/cec-notifier.h>
17 #include <drm/drm_edid.h>
18 
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);
27 
28 	u16 phys_addr;
29 };
30 
31 static LIST_HEAD(cec_notifiers);
32 static DEFINE_MUTEX(cec_notifiers_lock);
33 
cec_notifier_get_conn(struct device * dev,const char * conn)34 struct cec_notifier *cec_notifier_get_conn(struct device *dev, const char *conn)
35 {
36 	struct cec_notifier *n;
37 
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;
45 		}
46 	}
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;
60 }
61 EXPORT_SYMBOL_GPL(cec_notifier_get_conn);
62 
cec_notifier_release(struct kref * kref)63 static void cec_notifier_release(struct kref *kref)
64 {
65 	struct cec_notifier *n =
66 		container_of(kref, struct cec_notifier, kref);
67 
68 	list_del(&n->head);
69 	kfree(n->conn);
70 	kfree(n);
71 }
72 
cec_notifier_put(struct cec_notifier * n)73 void cec_notifier_put(struct cec_notifier *n)
74 {
75 	mutex_lock(&cec_notifiers_lock);
76 	kref_put(&n->kref, cec_notifier_release);
77 	mutex_unlock(&cec_notifiers_lock);
78 }
79 EXPORT_SYMBOL_GPL(cec_notifier_put);
80 
cec_notifier_set_phys_addr(struct cec_notifier * n,u16 pa)81 void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa)
82 {
83 	if (n == NULL)
84 		return;
85 
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);
91 }
92 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr);
93 
cec_notifier_set_phys_addr_from_edid(struct cec_notifier * n,const struct edid * edid)94 void cec_notifier_set_phys_addr_from_edid(struct cec_notifier *n,
95 					  const struct edid *edid)
96 {
97 	u16 pa = CEC_PHYS_ADDR_INVALID;
98 
99 	if (n == NULL)
100 		return;
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 
cec_notifier_register(struct cec_notifier * n,struct cec_adapter * adap,void (* callback)(struct cec_adapter * adap,u16 pa))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 
cec_notifier_unregister(struct cec_notifier * n)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);
130