brintos

brintos / linux-shallow public Read only

0
0
Text · 5.7 KiB · 2d6b7b0 Raw
213 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * HD audio Component Binding Interface4 *5 * Copyright (C) 2021, 2023 Cirrus Logic, Inc. and6 *			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_ACPI18void 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");213