Path: blob/master/sound/hda/codecs/side-codecs/hda_component.c
29270 views
// SPDX-License-Identifier: GPL-2.0-or-later1/*2* HD audio Component Binding Interface3*4* Copyright (C) 2021, 2023 Cirrus Logic, Inc. and5* Cirrus Logic International Semiconductor Ltd.6*/78#include <linux/acpi.h>9#include <linux/component.h>10#include <linux/module.h>11#include <linux/slab.h>12#include <sound/hda_codec.h>13#include "hda_component.h"14#include "hda_local.h"1516#ifdef CONFIG_ACPI17void hda_component_acpi_device_notify(struct hda_component_parent *parent,18acpi_handle handle, u32 event, void *data)19{20struct hda_component *comp;21int i;2223guard(mutex)(&parent->mutex);24for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {25comp = hda_component_from_index(parent, i);26if (comp->dev && comp->acpi_notify)27comp->acpi_notify(acpi_device_handle(comp->adev), event, comp->dev);28}29}30EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, "SND_HDA_SCODEC_COMPONENT");3132int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,33struct hda_component_parent *parent,34acpi_notify_handler handler, void *data)35{36bool support_notifications = false;37struct acpi_device *adev;38struct hda_component *comp;39int ret;40int i;4142adev = parent->comps[0].adev;43if (!acpi_device_handle(adev))44return 0;4546for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {47comp = hda_component_from_index(parent, i);48support_notifications = support_notifications ||49comp->acpi_notifications_supported;50}5152if (support_notifications) {53ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,54handler, data);55if (ret < 0) {56codec_warn(cdc, "Failed to install notify handler: %d\n", ret);57return 0;58}5960codec_dbg(cdc, "Notify handler installed\n");61}6263return 0;64}65EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT");6667void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,68struct hda_component_parent *parent,69acpi_notify_handler handler)70{71struct acpi_device *adev;72int ret;7374adev = parent->comps[0].adev;75if (!acpi_device_handle(adev))76return;7778ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, handler);79if (ret < 0)80codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret);81}82EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT");83#endif /* ifdef CONFIG_ACPI */8485void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action)86{87struct hda_component *comp;88int i;8990guard(mutex)(&parent->mutex);91for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {92comp = hda_component_from_index(parent, i);93if (comp->dev && comp->pre_playback_hook)94comp->pre_playback_hook(comp->dev, action);95}96for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {97comp = hda_component_from_index(parent, i);98if (comp->dev && comp->playback_hook)99comp->playback_hook(comp->dev, action);100}101for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {102comp = hda_component_from_index(parent, i);103if (comp->dev && comp->post_playback_hook)104comp->post_playback_hook(comp->dev, action);105}106}107EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, "SND_HDA_SCODEC_COMPONENT");108109struct hda_scodec_match {110const char *bus;111const char *hid;112const char *match_str;113int index;114};115116/* match the device name in a slightly relaxed manner */117static int hda_comp_match_dev_name(struct device *dev, void *data)118{119struct hda_scodec_match *p = data;120const char *d = dev_name(dev);121int n = strlen(p->bus);122char tmp[32];123124/* check the bus name */125if (strncmp(d, p->bus, n))126return 0;127/* skip the bus number */128if (isdigit(d[n]))129n++;130/* the rest must be exact matching */131snprintf(tmp, sizeof(tmp), p->match_str, p->hid, p->index);132return !strcmp(d + n, tmp);133}134135int hda_component_manager_bind(struct hda_codec *cdc,136struct hda_component_parent *parent)137{138/* Init shared and component specific data */139memset(parent->comps, 0, sizeof(parent->comps));140141guard(mutex)(&parent->mutex);142return component_bind_all(hda_codec_dev(cdc), parent);143}144EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind, "SND_HDA_SCODEC_COMPONENT");145146int hda_component_manager_init(struct hda_codec *cdc,147struct hda_component_parent *parent, int count,148const char *bus, const char *hid,149const char *match_str,150const struct component_master_ops *ops)151{152struct device *dev = hda_codec_dev(cdc);153struct component_match *match = NULL;154struct hda_scodec_match *sm;155int ret, i;156157if (parent->codec) {158codec_err(cdc, "Component binding already created (SSID: %x)\n",159cdc->core.subsystem_id);160return -EINVAL;161}162parent->codec = cdc;163164mutex_init(&parent->mutex);165166for (i = 0; i < count; i++) {167sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL);168if (!sm)169return -ENOMEM;170171sm->bus = bus;172sm->hid = hid;173sm->match_str = match_str;174sm->index = i;175component_match_add(dev, &match, hda_comp_match_dev_name, sm);176}177178ret = component_master_add_with_match(dev, ops, match);179if (ret)180codec_err(cdc, "Fail to register component aggregator %d\n", ret);181182return ret;183}184EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, "SND_HDA_SCODEC_COMPONENT");185186void hda_component_manager_free(struct hda_component_parent *parent,187const struct component_master_ops *ops)188{189struct device *dev;190191if (!parent->codec)192return;193194dev = hda_codec_dev(parent->codec);195196component_master_del(dev, ops);197198parent->codec = NULL;199}200EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, "SND_HDA_SCODEC_COMPONENT");201202MODULE_DESCRIPTION("HD Audio component binding library");203MODULE_AUTHOR("Richard Fitzgerald <[email protected]>");204MODULE_LICENSE("GPL");205206207