xref: /linux/sound/pci/hda/hda_component.c (revision f898c16a0624e7f2dcb0b1cda6916c9be6489197)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * HD audio Component Binding Interface
4  *
5  * Copyright (C) 2021, 2023 Cirrus Logic, Inc. and
6  *			Cirrus Logic International Semiconductor Ltd.
7  */
8 
9 #include <linux/acpi.h>
10 #include <linux/component.h>
11 #include <linux/module.h>
12 #include <linux/slab.h>
13 #include <sound/hda_codec.h>
14 #include "hda_component.h"
15 #include "hda_local.h"
16 
17 #ifdef CONFIG_ACPI
18 void hda_component_acpi_device_notify(struct hda_component *comps, int num_comps,
19 				      acpi_handle handle, u32 event, void *data)
20 {
21 	int i;
22 
23 	for (i = 0; i < num_comps; i++) {
24 		if (comps[i].dev && comps[i].acpi_notify)
25 			comps[i].acpi_notify(acpi_device_handle(comps[i].adev), event,
26 					     comps[i].dev);
27 	}
28 }
29 EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, SND_HDA_SCODEC_COMPONENT);
30 
31 int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
32 						  struct hda_component *comps, int num_comps,
33 						  acpi_notify_handler handler, void *data)
34 {
35 	bool support_notifications = false;
36 	struct acpi_device *adev;
37 	int ret;
38 	int i;
39 
40 	adev = comps[0].adev;
41 	if (!acpi_device_handle(adev))
42 		return 0;
43 
44 	for (i = 0; i < num_comps; i++)
45 		support_notifications = support_notifications ||
46 			comps[i].acpi_notifications_supported;
47 
48 	if (support_notifications) {
49 		ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
50 						  handler, data);
51 		if (ret < 0) {
52 			codec_warn(cdc, "Failed to install notify handler: %d\n", ret);
53 			return 0;
54 		}
55 
56 		codec_dbg(cdc, "Notify handler installed\n");
57 	}
58 
59 	return 0;
60 }
61 EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, SND_HDA_SCODEC_COMPONENT);
62 
63 void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
64 						     struct hda_component *comps,
65 						     acpi_notify_handler handler)
66 {
67 	struct acpi_device *adev;
68 	int ret;
69 
70 	adev = comps[0].adev;
71 	if (!acpi_device_handle(adev))
72 		return;
73 
74 	ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, handler);
75 	if (ret < 0)
76 		codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret);
77 }
78 EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, SND_HDA_SCODEC_COMPONENT);
79 #endif /* ifdef CONFIG_ACPI */
80 
81 void hda_component_manager_playback_hook(struct hda_component *comps, int num_comps, int action)
82 {
83 	int i;
84 
85 	for (i = 0; i < num_comps; i++) {
86 		if (comps[i].dev && comps[i].pre_playback_hook)
87 			comps[i].pre_playback_hook(comps[i].dev, action);
88 	}
89 	for (i = 0; i < num_comps; i++) {
90 		if (comps[i].dev && comps[i].playback_hook)
91 			comps[i].playback_hook(comps[i].dev, action);
92 	}
93 	for (i = 0; i < num_comps; i++) {
94 		if (comps[i].dev && comps[i].post_playback_hook)
95 			comps[i].post_playback_hook(comps[i].dev, action);
96 	}
97 }
98 EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, SND_HDA_SCODEC_COMPONENT);
99 
100 struct hda_scodec_match {
101 	const char *bus;
102 	const char *hid;
103 	const char *match_str;
104 	int index;
105 };
106 
107 /* match the device name in a slightly relaxed manner */
108 static int hda_comp_match_dev_name(struct device *dev, void *data)
109 {
110 	struct hda_scodec_match *p = data;
111 	const char *d = dev_name(dev);
112 	int n = strlen(p->bus);
113 	char tmp[32];
114 
115 	/* check the bus name */
116 	if (strncmp(d, p->bus, n))
117 		return 0;
118 	/* skip the bus number */
119 	if (isdigit(d[n]))
120 		n++;
121 	/* the rest must be exact matching */
122 	snprintf(tmp, sizeof(tmp), p->match_str, p->hid, p->index);
123 	return !strcmp(d + n, tmp);
124 }
125 
126 int hda_component_manager_bind(struct hda_codec *cdc,
127 			       struct hda_component *comps, int count)
128 {
129 	int i;
130 
131 	/* Init shared data */
132 	for (i = 0; i < count; ++i) {
133 		memset(&comps[i], 0, sizeof(comps[i]));
134 		comps[i].codec = cdc;
135 	}
136 
137 	return component_bind_all(hda_codec_dev(cdc), comps);
138 }
139 EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind, SND_HDA_SCODEC_COMPONENT);
140 
141 int hda_component_manager_init(struct hda_codec *cdc,
142 			       struct hda_component *comps, int count,
143 			       const char *bus, const char *hid,
144 			       const char *match_str,
145 			       const struct component_master_ops *ops)
146 {
147 	struct device *dev = hda_codec_dev(cdc);
148 	struct component_match *match = NULL;
149 	struct hda_scodec_match *sm;
150 	int ret, i;
151 
152 	for (i = 0; i < count; i++) {
153 		sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL);
154 		if (!sm)
155 			return -ENOMEM;
156 
157 		sm->bus = bus;
158 		sm->hid = hid;
159 		sm->match_str = match_str;
160 		sm->index = i;
161 		component_match_add(dev, &match, hda_comp_match_dev_name, sm);
162 	}
163 
164 	ret = component_master_add_with_match(dev, ops, match);
165 	if (ret)
166 		codec_err(cdc, "Fail to register component aggregator %d\n", ret);
167 
168 	return ret;
169 }
170 EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, SND_HDA_SCODEC_COMPONENT);
171 
172 void hda_component_manager_free(struct hda_codec *cdc,
173 				const struct component_master_ops *ops)
174 {
175 	struct device *dev = hda_codec_dev(cdc);
176 
177 	component_master_del(dev, ops);
178 }
179 EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, SND_HDA_SCODEC_COMPONENT);
180 
181 MODULE_DESCRIPTION("HD Audio component binding library");
182 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
183 MODULE_LICENSE("GPL");
184