source: GPL/trunk/alsa-kernel/pci/hda/hda_component.c

Last change on this file was 777, checked in by David Azarewicz, 7 months ago

Merge from uniaud32-exp branch

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