File Index
Files and directories
Sign in
FILE

hda_component.c

File preview with syntax highlighting.

linux-6.19.14/sound/hda/codecs/side-codecs/hda_component.c C
  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
 18void 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}
 31EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, "SND_HDA_SCODEC_COMPONENT");
 32
 33int 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}
 66EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT");
 67
 68void 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}
 83EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT");
 84#endif /* ifdef CONFIG_ACPI */
 85
 86void 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}
108EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, "SND_HDA_SCODEC_COMPONENT");
109
110struct 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 */
118static 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
136int 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}
145EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind, "SND_HDA_SCODEC_COMPONENT");
146
147int 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		if (IS_ERR(match)) {
178			codec_err(cdc, "Fail to add component %ld\n", PTR_ERR(match));
179			return PTR_ERR(match);
180		}
181	}
182
183	ret = component_master_add_with_match(dev, ops, match);
184	if (ret)
185		codec_err(cdc, "Fail to register component aggregator %d\n", ret);
186
187	return ret;
188}
189EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, "SND_HDA_SCODEC_COMPONENT");
190
191void hda_component_manager_free(struct hda_component_parent *parent,
192				const struct component_master_ops *ops)
193{
194	struct device *dev;
195
196	if (!parent->codec)
197		return;
198
199	dev = hda_codec_dev(parent->codec);
200
201	component_master_del(dev, ops);
202
203	parent->codec = NULL;
204}
205EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, "SND_HDA_SCODEC_COMPONENT");
206
207MODULE_DESCRIPTION("HD Audio component binding library");
208MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
209MODULE_LICENSE("GPL");
~/ Read-only file preview.