ALSA: hda/realtek: Support ACPI Notification framework via component binding

For systems which have support for ACPI notifications, add a mechanism to
register a handler for ACPI notifications and then call the acpi_notify
api on the bound components.

Registering a handler in the Realtek HDA driver, allows a single handler to
be registered, which then calls into all the components, rather than
attempting to register the same handler multiple times, once for each
component.

Signed-off-by: Stefan Binding <sbinding@opensource.cirrus.com>
Link: https://lore.kernel.org/r/20230921162849.1988124-3-sbinding@opensource.cirrus.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Stefan Binding 2023-09-21 17:28:47 +01:00 committed by Takashi Iwai
parent 502629a755
commit 7ce669334c

View File

@ -10,6 +10,7 @@
* Jonathan Woithe <jwoithe@just42.net>
*/
#include <linux/acpi.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
@ -6704,12 +6705,91 @@ static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec,
}
}
#ifdef CONFIG_ACPI
static void comp_acpi_device_notify(acpi_handle handle, u32 event, void *data)
{
struct hda_codec *cdc = data;
struct alc_spec *spec = cdc->spec;
int i;
codec_info(cdc, "ACPI Notification %d\n", event);
for (i = 0; i < HDA_MAX_COMPONENTS; i++) {
if (spec->comps[i].dev && spec->comps[i].acpi_notify)
spec->comps[i].acpi_notify(acpi_device_handle(spec->comps[i].adev), event,
spec->comps[i].dev);
}
}
static int comp_bind_acpi(struct device *dev)
{
struct hda_codec *cdc = dev_to_hda_codec(dev);
struct alc_spec *spec = cdc->spec;
bool support_notifications = false;
struct acpi_device *adev;
int ret;
int i;
adev = spec->comps[0].adev;
if (!acpi_device_handle(adev))
return 0;
for (i = 0; i < HDA_MAX_COMPONENTS; i++)
support_notifications = support_notifications ||
spec->comps[i].acpi_notifications_supported;
if (support_notifications) {
ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
comp_acpi_device_notify, cdc);
if (ret < 0) {
codec_warn(cdc, "Failed to install notify handler: %d\n", ret);
return 0;
}
codec_dbg(cdc, "Notify handler installed\n");
}
return 0;
}
static void comp_unbind_acpi(struct device *dev)
{
struct hda_codec *cdc = dev_to_hda_codec(dev);
struct alc_spec *spec = cdc->spec;
struct acpi_device *adev;
int ret;
adev = spec->comps[0].adev;
if (!acpi_device_handle(adev))
return;
ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
comp_acpi_device_notify);
if (ret < 0)
codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret);
}
#else
static int comp_bind_acpi(struct device *dev)
{
return 0;
}
static void comp_unbind_acpi(struct device *dev)
{
}
#endif
static int comp_bind(struct device *dev)
{
struct hda_codec *cdc = dev_to_hda_codec(dev);
struct alc_spec *spec = cdc->spec;
int ret;
return component_bind_all(dev, spec->comps);
ret = component_bind_all(dev, spec->comps);
if (ret)
return ret;
return comp_bind_acpi(dev);
}
static void comp_unbind(struct device *dev)
@ -6717,6 +6797,7 @@ static void comp_unbind(struct device *dev)
struct hda_codec *cdc = dev_to_hda_codec(dev);
struct alc_spec *spec = cdc->spec;
comp_unbind_acpi(dev);
component_unbind_all(dev, spec->comps);
}