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_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 } 156 EXPORT_SYMBOL_NS_GPL(hda_component_manager_init, SND_HDA_SCODEC_COMPONENT); 157 158 void 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 } 165 EXPORT_SYMBOL_NS_GPL(hda_component_manager_free, SND_HDA_SCODEC_COMPONENT); 166 167 MODULE_DESCRIPTION("HD Audio component binding library"); 168 MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); 169 MODULE_LICENSE("GPL"); 170