Path: blob/master/sound/hda/codecs/side-codecs/hda_component.c
26481 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;2223mutex_lock(&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}29mutex_unlock(&parent->mutex);30}31EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, "SND_HDA_SCODEC_COMPONENT");3233int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc,34struct hda_component_parent *parent,35acpi_notify_handler handler, void *data)36{37bool support_notifications = false;38struct acpi_device *adev;39struct hda_component *comp;40int ret;41int i;4243adev = parent->comps[0].adev;44if (!acpi_device_handle(adev))45return 0;4647for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {48comp = hda_component_from_index(parent, i);49support_notifications = support_notifications ||50comp->acpi_notifications_supported;51}5253if (support_notifications) {54ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,55handler, data);56if (ret < 0) {57codec_warn(cdc, "Failed to install notify handler: %d\n", ret);58return 0;59}6061codec_dbg(cdc, "Notify handler installed\n");62}6364return 0;65}66EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, "SND_HDA_SCODEC_COMPONENT");6768void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc,69struct hda_component_parent *parent,70acpi_notify_handler handler)71{72struct acpi_device *adev;73int ret;7475adev = parent->comps[0].adev;76if (!acpi_device_handle(adev))77return;7879ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, handler);80if (ret < 0)81codec_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 */8586void hda_component_manager_playback_hook(struct hda_component_parent *parent, int action)87{88struct hda_component *comp;89int i;9091mutex_lock(&parent->mutex);92for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {93comp = hda_component_from_index(parent, i);94if (comp->dev && comp->pre_playback_hook)95comp->pre_playback_hook(comp->dev, action);96}97for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {98comp = hda_component_from_index(parent, i);99if (comp->dev && comp->playback_hook)100comp->playback_hook(comp->dev, action);101}102for (i = 0; i < ARRAY_SIZE(parent->comps); i++) {103comp = hda_component_from_index(parent, i);104if (comp->dev && comp->post_playback_hook)105comp->post_playback_hook(comp->dev, action);106}107mutex_unlock(&parent->mutex);108}109EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, "SND_HDA_SCODEC_COMPONENT");110111struct hda_scodec_match {112const char *bus;113const char *hid;114const char *match_str;115int index;116};117118/* match the device name in a slightly relaxed manner */119static int hda_comp_match_dev_name(struct device *dev, void *data)120{121struct hda_scodec_match *p = data;122const char *d = dev_name(dev);123int n = strlen(p->bus);124char tmp[32];125126/* check the bus name */127if (strncmp(d, p->bus, n))128return 0;129/* skip the bus number */130if (isdigit(d[n]))131n++;132/* the rest must be exact matching */133snprintf(tmp, sizeof(tmp), p->match_str, p->hid, p->index);134return !strcmp(d + n, tmp);135}136137int hda_component_manager_bind(struct hda_codec *cdc,138struct hda_component_parent *parent)139{140int ret;141142/* Init shared and component specific data */143memset(parent->comps, 0, sizeof(parent->comps));144145mutex_lock(&parent->mutex);146ret = component_bind_all(hda_codec_dev(cdc), parent);147mutex_unlock(&parent->mutex);148149return ret;150}151EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind, "SND_HDA_SCODEC_COMPONENT");152153int hda_component_manager_init(struct hda_codec *cdc,154struct hda_component_parent *parent, int count,155const char *bus, const char *hid,156const char *match_str,157const struct component_master_ops *ops)158{159struct device *dev = hda_codec_dev(cdc);160struct component_match *match = NULL;161struct hda_scodec_match *sm;162int ret, i;163164if (parent->codec) {165codec_err(cdc, "Component binding already created (SSID: %x)\n",166cdc->core.subsystem_id);167return -EINVAL;168}169parent->codec = cdc;170171mutex_init(&parent->mutex);172173for (i = 0; i < count; i++) {174sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL);175if (!sm)176return -ENOMEM;177178sm->bus = bus;179sm->hid = hid;180sm->match_str = match_str;181sm->index = i;182component_match_add(dev, &match, hda_comp_match_dev_name, sm);183}184185ret = component_master_add_with_match(dev, ops, match);186if (ret)187codec_err(cdc, "Fail to register component aggregator %d\n", ret);188189return ret;190}191EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, "SND_HDA_SCODEC_COMPONENT");192193void hda_component_manager_free(struct hda_component_parent *parent,194const struct component_master_ops *ops)195{196struct device *dev;197198if (!parent->codec)199return;200201dev = hda_codec_dev(parent->codec);202203component_master_del(dev, ops);204205parent->codec = NULL;206}207EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, "SND_HDA_SCODEC_COMPONENT");208209MODULE_DESCRIPTION("HD Audio component binding library");210MODULE_AUTHOR("Richard Fitzgerald <[email protected]>");211MODULE_LICENSE("GPL");212213214