Commit | Line | Data |
---|---|---|
b4e30a9e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
6917a7b7 HV |
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. | |
6917a7b7 HV |
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 | ||
ee7e9871 | 15 | #include <media/cec.h> |
6917a7b7 HV |
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; | |
7a78c1e1 | 24 | const char *conn; |
6917a7b7 HV |
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 | ||
7a78c1e1 | 34 | struct cec_notifier *cec_notifier_get_conn(struct device *dev, const char *conn) |
6917a7b7 HV |
35 | { |
36 | struct cec_notifier *n; | |
37 | ||
38 | mutex_lock(&cec_notifiers_lock); | |
39 | list_for_each_entry(n, &cec_notifiers, head) { | |
7a78c1e1 NA |
40 | if (n->dev == dev && |
41 | (!conn || !strcmp(n->conn, conn))) { | |
6917a7b7 HV |
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; | |
7a78c1e1 NA |
51 | if (conn) |
52 | n->conn = kstrdup(conn, GFP_KERNEL); | |
6917a7b7 HV |
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 | } | |
7a78c1e1 | 61 | EXPORT_SYMBOL_GPL(cec_notifier_get_conn); |
6917a7b7 HV |
62 | |
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); | |
7a78c1e1 | 69 | kfree(n->conn); |
6917a7b7 HV |
70 | kfree(n); |
71 | } | |
72 | ||
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 | ||
81 | void cec_notifier_set_phys_addr(struct cec_notifier *n, u16 pa) | |
82 | { | |
fc1ff45a HV |
83 | if (n == NULL) |
84 | return; | |
85 | ||
6917a7b7 HV |
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 | ||
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 | ||
fc1ff45a HV |
99 | if (n == NULL) |
100 | return; | |
101 | ||
6917a7b7 HV |
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); |