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 18 void 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 } 29 EXPORT_SYMBOL_NS_GPL(hda_component_acpi_device_notify, SND_HDA_SCODEC_COMPONENT); 30 31 int 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 } 61 EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind_acpi_notifications, SND_HDA_SCODEC_COMPONENT); 62 63 void 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 } 78 EXPORT_SYMBOL_NS_GPL(hda_component_manager_unbind_acpi_notifications, SND_HDA_SCODEC_COMPONENT); 79 #endif /* ifdef CONFIG_ACPI */ 80 81 void 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 } 98 EXPORT_SYMBOL_NS_GPL(hda_component_manager_playback_hook, SND_HDA_SCODEC_COMPONENT); 99 100 struct 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 */ 108 static 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 126 int hda_component_manager_bind(struct hda_codec *cdc, 127 struct hda_component *comps, int count) 128 { 129 int i; 130 131 /* Init shared data */ 132 for (i = 0; i < count; ++i) { 133 memset(&comps[i], 0, sizeof(comps[i])); 134 comps[i].codec = cdc; 135 } 136 137 return component_bind_all(hda_codec_dev(cdc), comps); 138 } 139 EXPORT_SYMBOL_NS_GPL(hda_component_manager_bind, SND_HDA_SCODEC_COMPONENT); 140 141 int hda_component_manager_init(struct hda_codec *cdc, 142 struct hda_component *comps, int count, 143 const char *bus, const char *hid, 144 const char *match_str, 145 const struct component_master_ops *ops) 146 { 147 struct device *dev = hda_codec_dev(cdc); 148 struct component_match *match = NULL; 149 struct hda_scodec_match *sm; 150 int ret, i; 151 152 for (i = 0; i < count; i++) { 153 sm = devm_kmalloc(dev, sizeof(*sm), GFP_KERNEL); 154 if (!sm) 155 return -ENOMEM; 156 157 sm->bus = bus; 158 sm->hid = hid; 159 sm->match_str = match_str; 160 sm->index = i; 161 component_match_add(dev, &match, hda_comp_match_dev_name, sm); 162 } 163 164 ret = component_master_add_with_match(dev, ops, match); 165 if (ret) 166 codec_err(cdc, "Fail to register component aggregator %d\n", ret); 167 168 return ret; 169 } 170 EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, SND_HDA_SCODEC_COMPONENT); 171 172 void hda_component_manager_free(struct hda_codec *cdc, 173 const struct component_master_ops *ops) 174 { 175 struct device *dev = hda_codec_dev(cdc); 176 177 component_master_del(dev, ops); 178 } 179 EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, SND_HDA_SCODEC_COMPONENT); 180 181 MODULE_DESCRIPTION("HD Audio component binding library"); 182 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); 183 MODULE_LICENSE("GPL"); 184