1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * HD audio Component Binding Interface
5 * Copyright (C) 2021, 2023 Cirrus Logic, Inc. and
6 * Cirrus Logic International Semiconductor Ltd.
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"
18 void hda_component_acpi_device_notify(struct hda_component_parent
*parent
,
19 acpi_handle handle
, u32 event
, void *data
)
21 struct hda_component
*comp
;
24 mutex_lock(&parent
->mutex
);
25 for (i
= 0; i
< ARRAY_SIZE(parent
->comps
); i
++) {
26 comp
= hda_component_from_index(parent
, i
);
27 if (comp
->dev
&& comp
->acpi_notify
)
28 comp
->acpi_notify(acpi_device_handle(comp
->adev
), event
, comp
->dev
);
30 mutex_unlock(&parent
->mutex
);
32 EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify
, "SND_HDA_SCODEC_COMPONENT");
34 int hda_component_manager_bind_acpi_notifications(struct hda_codec
*cdc
,
35 struct hda_component_parent
*parent
,
36 acpi_notify_handler handler
, void *data
)
38 bool support_notifications
= false;
39 struct acpi_device
*adev
;
40 struct hda_component
*comp
;
44 adev
= parent
->comps
[0].adev
;
45 if (!acpi_device_handle(adev
))
48 for (i
= 0; i
< ARRAY_SIZE(parent
->comps
); i
++) {
49 comp
= hda_component_from_index(parent
, i
);
50 support_notifications
= support_notifications
||
51 comp
->acpi_notifications_supported
;
54 if (support_notifications
) {
55 ret
= acpi_install_notify_handler(adev
->handle
, ACPI_DEVICE_NOTIFY
,
58 codec_warn(cdc
, "Failed to install notify handler: %d\n", ret
);
62 codec_dbg(cdc
, "Notify handler installed\n");
67 EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications
, "SND_HDA_SCODEC_COMPONENT");
69 void hda_component_manager_unbind_acpi_notifications(struct hda_codec
*cdc
,
70 struct hda_component_parent
*parent
,
71 acpi_notify_handler handler
)
73 struct acpi_device
*adev
;
76 adev
= parent
->comps
[0].adev
;
77 if (!acpi_device_handle(adev
))
80 ret
= acpi_remove_notify_handler(adev
->handle
, ACPI_DEVICE_NOTIFY
, handler
);
82 codec_warn(cdc
, "Failed to uninstall notify handler: %d\n", ret
);
84 EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications
, "SND_HDA_SCODEC_COMPONENT");
85 #endif /* ifdef CONFIG_ACPI */
87 void hda_component_manager_playback_hook(struct hda_component_parent
*parent
, int action
)
89 struct hda_component
*comp
;
92 mutex_lock(&parent
->mutex
);
93 for (i
= 0; i
< ARRAY_SIZE(parent
->comps
); i
++) {
94 comp
= hda_component_from_index(parent
, i
);
95 if (comp
->dev
&& comp
->pre_playback_hook
)
96 comp
->pre_playback_hook(comp
->dev
, action
);
98 for (i
= 0; i
< ARRAY_SIZE(parent
->comps
); i
++) {
99 comp
= hda_component_from_index(parent
, i
);
100 if (comp
->dev
&& comp
->playback_hook
)
101 comp
->playback_hook(comp
->dev
, action
);
103 for (i
= 0; i
< ARRAY_SIZE(parent
->comps
); i
++) {
104 comp
= hda_component_from_index(parent
, i
);
105 if (comp
->dev
&& comp
->post_playback_hook
)
106 comp
->post_playback_hook(comp
->dev
, action
);
108 mutex_unlock(&parent
->mutex
);
110 EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook
, "SND_HDA_SCODEC_COMPONENT");
112 struct hda_scodec_match
{
115 const char *match_str
;
119 /* match the device name in a slightly relaxed manner */
120 static int hda_comp_match_dev_name(struct device
*dev
, void *data
)
122 struct hda_scodec_match
*p
= data
;
123 const char *d
= dev_name(dev
);
124 int n
= strlen(p
->bus
);
127 /* check the bus name */
128 if (strncmp(d
, p
->bus
, n
))
130 /* skip the bus number */
133 /* the rest must be exact matching */
134 snprintf(tmp
, sizeof(tmp
), p
->match_str
, p
->hid
, p
->index
);
135 return !strcmp(d
+ n
, tmp
);
138 int hda_component_manager_bind(struct hda_codec
*cdc
,
139 struct hda_component_parent
*parent
)
143 /* Init shared and component specific data */
144 memset(parent
->comps
, 0, sizeof(parent
->comps
));
146 mutex_lock(&parent
->mutex
);
147 ret
= component_bind_all(hda_codec_dev(cdc
), parent
);
148 mutex_unlock(&parent
->mutex
);
152 EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind
, "SND_HDA_SCODEC_COMPONENT");
154 int hda_component_manager_init(struct hda_codec
*cdc
,
155 struct hda_component_parent
*parent
, int count
,
156 const char *bus
, const char *hid
,
157 const char *match_str
,
158 const struct component_master_ops
*ops
)
160 struct device
*dev
= hda_codec_dev(cdc
);
161 struct component_match
*match
= NULL
;
162 struct hda_scodec_match
*sm
;
166 codec_err(cdc
, "Component binding already created (SSID: %x)\n",
167 cdc
->core
.subsystem_id
);
172 mutex_init(&parent
->mutex
);
174 for (i
= 0; i
< count
; i
++) {
175 sm
= devm_kmalloc(dev
, sizeof(*sm
), GFP_KERNEL
);
181 sm
->match_str
= match_str
;
183 component_match_add(dev
, &match
, hda_comp_match_dev_name
, sm
);
186 ret
= component_master_add_with_match(dev
, ops
, match
);
188 codec_err(cdc
, "Fail to register component aggregator %d\n", ret
);
192 EXPORT_SYMBOL_NS_GPL(hda_component_manager_init
, "SND_HDA_SCODEC_COMPONENT");
194 void hda_component_manager_free(struct hda_component_parent
*parent
,
195 const struct component_master_ops
*ops
)
202 dev
= hda_codec_dev(parent
->codec
);
204 component_master_del(dev
, ops
);
206 parent
->codec
= NULL
;
208 EXPORT_SYMBOL_NS_GPL(hda_component_manager_free
, "SND_HDA_SCODEC_COMPONENT");
210 MODULE_DESCRIPTION("HD Audio component binding library");
211 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
212 MODULE_LICENSE("GPL");