1 // SPDX-License-Identifier: GPL-2.0-only
3 * cec-notifier.c - notify CEC drivers of physical address changes
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.
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 #include <linux/of_platform.h>
16 #include <media/cec.h>
17 #include <media/cec-notifier.h>
18 #include <drm/drm_edid.h>
22 struct list_head head
;
24 struct device
*hdmi_dev
;
25 struct cec_connector_info conn_info
;
26 const char *conn_name
;
27 struct cec_adapter
*cec_adap
;
32 static LIST_HEAD(cec_notifiers
);
33 static DEFINE_MUTEX(cec_notifiers_lock
);
36 cec_notifier_get_conn(struct device
*hdmi_dev
, const char *conn_name
)
38 struct cec_notifier
*n
;
40 mutex_lock(&cec_notifiers_lock
);
41 list_for_each_entry(n
, &cec_notifiers
, head
) {
42 if (n
->hdmi_dev
== hdmi_dev
&&
44 (n
->conn_name
&& !strcmp(n
->conn_name
, conn_name
)))) {
46 mutex_unlock(&cec_notifiers_lock
);
50 n
= kzalloc(sizeof(*n
), GFP_KERNEL
);
53 n
->hdmi_dev
= hdmi_dev
;
55 n
->conn_name
= kstrdup(conn_name
, GFP_KERNEL
);
62 n
->phys_addr
= CEC_PHYS_ADDR_INVALID
;
66 list_add_tail(&n
->head
, &cec_notifiers
);
68 mutex_unlock(&cec_notifiers_lock
);
71 EXPORT_SYMBOL_GPL(cec_notifier_get_conn
);
73 static void cec_notifier_release(struct kref
*kref
)
75 struct cec_notifier
*n
=
76 container_of(kref
, struct cec_notifier
, kref
);
83 static void cec_notifier_put(struct cec_notifier
*n
)
85 mutex_lock(&cec_notifiers_lock
);
86 kref_put(&n
->kref
, cec_notifier_release
);
87 mutex_unlock(&cec_notifiers_lock
);
91 cec_notifier_conn_register(struct device
*hdmi_dev
, const char *conn_name
,
92 const struct cec_connector_info
*conn_info
)
94 struct cec_notifier
*n
= cec_notifier_get_conn(hdmi_dev
, conn_name
);
100 n
->phys_addr
= CEC_PHYS_ADDR_INVALID
;
102 n
->conn_info
= *conn_info
;
104 memset(&n
->conn_info
, 0, sizeof(n
->conn_info
));
106 cec_phys_addr_invalidate(n
->cec_adap
);
107 cec_s_conn_info(n
->cec_adap
, conn_info
);
109 mutex_unlock(&n
->lock
);
112 EXPORT_SYMBOL_GPL(cec_notifier_conn_register
);
114 void cec_notifier_conn_unregister(struct cec_notifier
*n
)
119 mutex_lock(&n
->lock
);
120 memset(&n
->conn_info
, 0, sizeof(n
->conn_info
));
121 n
->phys_addr
= CEC_PHYS_ADDR_INVALID
;
123 cec_phys_addr_invalidate(n
->cec_adap
);
124 cec_s_conn_info(n
->cec_adap
, NULL
);
126 mutex_unlock(&n
->lock
);
129 EXPORT_SYMBOL_GPL(cec_notifier_conn_unregister
);
131 struct cec_notifier
*
132 cec_notifier_cec_adap_register(struct device
*hdmi_dev
, const char *conn_name
,
133 struct cec_adapter
*adap
)
135 struct cec_notifier
*n
;
140 n
= cec_notifier_get_conn(hdmi_dev
, conn_name
);
144 mutex_lock(&n
->lock
);
146 adap
->conn_info
= n
->conn_info
;
148 cec_s_phys_addr(adap
, n
->phys_addr
, false);
149 mutex_unlock(&n
->lock
);
152 EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_register
);
154 void cec_notifier_cec_adap_unregister(struct cec_notifier
*n
,
155 struct cec_adapter
*adap
)
160 mutex_lock(&n
->lock
);
161 adap
->notifier
= NULL
;
163 mutex_unlock(&n
->lock
);
166 EXPORT_SYMBOL_GPL(cec_notifier_cec_adap_unregister
);
168 void cec_notifier_set_phys_addr(struct cec_notifier
*n
, u16 pa
)
173 mutex_lock(&n
->lock
);
176 cec_s_phys_addr(n
->cec_adap
, n
->phys_addr
, false);
177 mutex_unlock(&n
->lock
);
179 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr
);
181 void cec_notifier_set_phys_addr_from_edid(struct cec_notifier
*n
,
182 const struct edid
*edid
)
184 u16 pa
= CEC_PHYS_ADDR_INVALID
;
189 if (edid
&& edid
->extensions
)
190 pa
= cec_get_edid_phys_addr((const u8
*)edid
,
191 EDID_LENGTH
* (edid
->extensions
+ 1), NULL
);
192 cec_notifier_set_phys_addr(n
, pa
);
194 EXPORT_SYMBOL_GPL(cec_notifier_set_phys_addr_from_edid
);
196 struct device
*cec_notifier_parse_hdmi_phandle(struct device
*dev
)
198 struct platform_device
*hdmi_pdev
;
199 struct device
*hdmi_dev
= NULL
;
200 struct device_node
*np
;
202 np
= of_parse_phandle(dev
->of_node
, "hdmi-phandle", 0);
205 dev_err(dev
, "Failed to find HDMI node in device tree\n");
206 return ERR_PTR(-ENODEV
);
208 hdmi_pdev
= of_find_device_by_node(np
);
211 hdmi_dev
= &hdmi_pdev
->dev
;
213 * Note that the device struct is only used as a key into the
214 * cec_notifiers list, it is never actually accessed.
215 * So we decrement the reference here so we don't leak
218 put_device(hdmi_dev
);
221 return ERR_PTR(-EPROBE_DEFER
);
223 EXPORT_SYMBOL_GPL(cec_notifier_parse_hdmi_phandle
);