Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/hda/codecs/side-codecs/hda_component.c
29270 views
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_parent *parent,
19
acpi_handle handle, u32 event, void *data)
20
{
21
struct hda_component *comp;
22
int i;
23
24
guard(mutex)(&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);
29
}
30
}
31
EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, "SND_HDA_SCODEC_COMPONENT");
32
33
int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,
34
struct hda_component_parent *parent,
35
acpi_notify_handler handler, void *data)
36
{
37
bool support_notifications = false;
38
struct acpi_device *adev;
39
struct hda_component *comp;
40
int ret;
41
int i;
42
43
adev = parent->comps[0].adev;
44
if (!acpi_device_handle(adev))
45
return 0;
46
47
for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
48
comp = hda_component_from_index(parent, i);
49
support_notifications = support_notifications ||
50
comp->acpi_notifications_supported;
51
}
52
53
if (support_notifications) {
54
ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
55
handler, data);
56
if (ret < 0) {
57
codec_warn(cdc, "Failed to install notify handler: %d\n", ret);
58
return 0;
59
}
60
61
codec_dbg(cdc, "Notify handler installed\n");
62
}
63
64
return 0;
65
}
66
EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT");
67
68
void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,
69
struct hda_component_parent *parent,
70
acpi_notify_handler handler)
71
{
72
struct acpi_device *adev;
73
int ret;
74
75
adev = parent->comps[0].adev;
76
if (!acpi_device_handle(adev))
77
return;
78
79
ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, handler);
80
if (ret < 0)
81
codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret);
82
}
83
EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT");
84
#endif /* ifdef CONFIG_ACPI */
85
86
void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action)
87
{
88
struct hda_component *comp;
89
int i;
90
91
guard(mutex)(&parent->mutex);
92
for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
93
comp = hda_component_from_index(parent, i);
94
if (comp->dev && comp->pre_playback_hook)
95
comp->pre_playback_hook(comp->dev, action);
96
}
97
for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
98
comp = hda_component_from_index(parent, i);
99
if (comp->dev && comp->playback_hook)
100
comp->playback_hook(comp->dev, action);
101
}
102
for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {
103
comp = hda_component_from_index(parent, i);
104
if (comp->dev && comp->post_playback_hook)
105
comp->post_playback_hook(comp->dev, action);
106
}
107
}
108
EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, "SND_HDA_SCODEC_COMPONENT");
109
110
struct hda_scodec_match {
111
const char *bus;
112
const char *hid;
113
const char *match_str;
114
int index;
115
};
116
117
/* match the device name in a slightly relaxed manner */
118
static int hda_comp_match_dev_name(struct device *dev, void *data)
119
{
120
struct hda_scodec_match *p = data;
121
const char *d = dev_name(dev);
122
int n = strlen(p->bus);
123
char tmp[32];
124
125
/* check the bus name */
126
if (strncmp(d, p->bus, n))
127
return 0;
128
/* skip the bus number */
129
if (isdigit(d[n]))
130
n++;
131
/* the rest must be exact matching */
132
snprintf(tmp, sizeof(tmp), p->match_str, p->hid, p->index);
133
return !strcmp(d + n, tmp);
134
}
135
136
int hda_component_manager_bind(struct hda_codec *cdc,
137
struct hda_component_parent *parent)
138
{
139
/* Init shared and component specific data */
140
memset(parent->comps, 0, sizeof(parent->comps));
141
142
guard(mutex)(&parent->mutex);
143
return component_bind_all(hda_codec_dev(cdc), parent);
144
}
145
EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind, "SND_HDA_SCODEC_COMPONENT");
146
147
int hda_component_manager_init(struct hda_codec *cdc,
148
struct hda_component_parent *parent, int count,
149
const char *bus, const char *hid,
150
const char *match_str,
151
const struct component_master_ops *ops)
152
{
153
struct device *dev = hda_codec_dev(cdc);
154
struct component_match *match = NULL;
155
struct hda_scodec_match *sm;
156
int ret, i;
157
158
if (parent->codec) {
159
codec_err(cdc, "Component binding already created (SSID: %x)\n",
160
cdc->core.subsystem_id);
161
return -EINVAL;
162
}
163
parent->codec = cdc;
164
165
mutex_init(&parent->mutex);
166
167
for (i = 0; i < count; i++) {
168
sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL);
169
if (!sm)
170
return -ENOMEM;
171
172
sm->bus = bus;
173
sm->hid = hid;
174
sm->match_str = match_str;
175
sm->index = i;
176
component_match_add(dev, &match, hda_comp_match_dev_name, sm);
177
}
178
179
ret = component_master_add_with_match(dev, ops, match);
180
if (ret)
181
codec_err(cdc, "Fail to register component aggregator %d\n", ret);
182
183
return ret;
184
}
185
EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, "SND_HDA_SCODEC_COMPONENT");
186
187
void hda_component_manager_free(struct hda_component_parent *parent,
188
const struct component_master_ops *ops)
189
{
190
struct device *dev;
191
192
if (!parent->codec)
193
return;
194
195
dev = hda_codec_dev(parent->codec);
196
197
component_master_del(dev, ops);
198
199
parent->codec = NULL;
200
}
201
EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, "SND_HDA_SCODEC_COMPONENT");
202
203
MODULE_DESCRIPTION("HD Audio component binding library");
204
MODULE_AUTHOR("Richard Fitzgerald <[email protected]>");
205
MODULE_LICENSE("GPL");
206
207