Linux Audio

Check our new training course

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