1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * HD audio Component Binding Interface 4 * 5 * Copyright (C) 2021, 2023 Cirrus Logic, Inc. and 6 * 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_ACPI 18void hda_component_acpi_device_notify(struct hda_component *comps, int num_comps, 19 acpi_handle handle, u32 event, void *data) 20{ 21 int i; 22 23 for (i = 0; i < num_comps; i++) { 24 if (comps[i].dev && comps[i].acpi_notify) 25 comps[i].acpi_notify(acpi_device_handle(comps[i].adev), event, 26 comps[i].dev); 27 } 28} 29EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, SND_HDA_SCODEC_COMPONENT); 30 31int hda_component_manager_bind_acpi_notifications(struct hda_codec *cdc, 32 struct hda_component *comps, int num_comps, 33 acpi_notify_handler handler, void *data) 34{ 35 bool support_notifications = false; 36 struct acpi_device *adev; 37 int ret; 38 int i; 39 40 adev = comps[0].adev; 41 if (!acpi_device_handle(adev)) 42 return 0; 43 44 for (i = 0; i < num_comps; i++) 45 support_notifications = support_notifications || 46 comps[i].acpi_notifications_supported; 47 48 if (support_notifications) { 49 ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, 50 handler, data); 51 if (ret < 0) { 52 codec_warn(cdc, "Failed to install notify handler: %d\n", ret); 53 return 0; 54 } 55 56 codec_dbg(cdc, "Notify handler installed\n"); 57 } 58 59 return 0; 60} 61EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, SND_HDA_SCODEC_COMPONENT); 62 63void hda_component_manager_unbind_acpi_notifications(struct hda_codec *cdc, 64 struct hda_component *comps, 65 acpi_notify_handler handler) 66{ 67 struct acpi_device *adev; 68 int ret; 69 70 adev = comps[0].adev; 71 if (!acpi_device_handle(adev)) 72 return; 73 74 ret = acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, handler); 75 if (ret < 0) 76 codec_warn(cdc, "Failed to uninstall notify handler: %d\n", ret); 77} 78EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, SND_HDA_SCODEC_COMPONENT); 79#endif /* ifdef CONFIG_ACPI */ 80 81void hda_component_manager_playback_hook(struct hda_component *comps, int num_comps, int action) 82{ 83 int i; 84 85 for (i = 0; i < num_comps; i++) { 86 if (comps[i].dev && comps[i].pre_playback_hook) 87 comps[i].pre_playback_hook(comps[i].dev, action); 88 } 89 for (i = 0; i < num_comps; i++) { 90 if (comps[i].dev && comps[i].playback_hook) 91 comps[i].playback_hook(comps[i].dev, action); 92 } 93 for (i = 0; i < num_comps; i++) { 94 if (comps[i].dev && comps[i].post_playback_hook) 95 comps[i].post_playback_hook(comps[i].dev, action); 96 } 97} 98EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, SND_HDA_SCODEC_COMPONENT); 99 100struct hda_scodec_match { 101 const char *bus; 102 const char *hid; 103 const char *match_str; 104 int index; 105}; 106 107/* match the device name in a slightly relaxed manner */ 108static int hda_comp_match_dev_name(struct device *dev, void *data) 109{ 110 struct hda_scodec_match *p = data; 111 const char *d = dev_name(dev); 112 int n = strlen(p->bus); 113 char tmp[32]; 114 115 /* check the bus name */ 116 if (strncmp(d, p->bus, n)) 117 return 0; 118 /* skip the bus number */ 119 if (isdigit(d[n])) 120 n++; 121 /* the rest must be exact matching */ 122 snprintf(tmp, sizeof(tmp), p->match_str, p->hid, p->index); 123 return !strcmp(d + n, tmp); 124} 125 126int hda_component_manager_init(struct hda_codec *cdc, 127 struct hda_component *comps, int count, 128 const char *bus, const char *hid, 129 const char *match_str, 130 const struct component_master_ops *ops) 131{ 132 struct device *dev = hda_codec_dev(cdc); 133 struct component_match *match = NULL; 134 struct hda_scodec_match *sm; 135 int ret, i; 136 137 for (i = 0; i < count; i++) { 138 sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL); 139 if (!sm) 140 return -ENOMEM; 141 142 sm->bus = bus; 143 sm->hid = hid; 144 sm->match_str = match_str; 145 sm->index = i; 146 comps[i].codec = cdc; 147 component_match_add(dev, &match, hda_comp_match_dev_name, sm); 148 } 149 150 ret = component_master_add_with_match(dev, ops, match); 151 if (ret) 152 codec_err(cdc, "Fail to register component aggregator %d\n", ret); 153 154 return ret; 155} 156EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, SND_HDA_SCODEC_COMPONENT); 157 158void hda_component_manager_free(struct hda_codec *cdc, 159 const struct component_master_ops *ops) 160{ 161 struct device *dev = hda_codec_dev(cdc); 162 163 component_master_del(dev, ops); 164} 165EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, SND_HDA_SCODEC_COMPONENT); 166 167MODULE_DESCRIPTION("HD Audio component binding library"); 168MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); 169MODULE_LICENSE("GPL"); 170