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