Merge tag 'scsi-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[linux-2.6-block.git] / drivers / media / cec / cec-notifier.c
CommitLineData
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
19struct 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
31static LIST_HEAD(cec_notifiers);
32static DEFINE_MUTEX(cec_notifiers_lock);
33
7a78c1e1 34struct 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);
57unlock:
58 mutex_unlock(&cec_notifiers_lock);
59 return n;
60}
7a78c1e1 61EXPORT_SYMBOL_GPL(cec_notifier_get_conn);
6917a7b7
HV
62
63static 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
73void 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}
79EXPORT_SYMBOL_GPL(cec_notifier_put);
80
81void 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}
92EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr);
93
94void 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}
107EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid);
108
109void 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}
120EXPORT_SYMBOL_GPL(cec_notifier_register);
121
122void 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}
129EXPORT_SYMBOL_GPL(cec_notifier_unregister);