1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 2 // Copyright(c) 2024 Intel Corporation. 3 4 /* 5 * SDCA Function Device management 6 */ 7 8 #include <linux/acpi.h> 9 #include <linux/module.h> 10 #include <linux/auxiliary_bus.h> 11 #include <linux/soundwire/sdw.h> 12 #include <sound/sdca.h> 13 #include <sound/sdca_function.h> 14 #include "sdca_function_device.h" 15 16 /* 17 * A SoundWire device can have multiple SDCA functions identified by 18 * their type and ADR. there can be multiple SoundWire devices per 19 * link, or multiple devices spread across multiple links. An IDA is 20 * required to identify each instance. 21 */ 22 static DEFINE_IDA(sdca_function_ida); 23 24 static void sdca_dev_release(struct device *dev) 25 { 26 struct auxiliary_device *auxdev = to_auxiliary_dev(dev); 27 struct sdca_dev *sdev = auxiliary_dev_to_sdca_dev(auxdev); 28 29 ida_free(&sdca_function_ida, auxdev->id); 30 kfree(sdev); 31 } 32 33 /* alloc, init and add link devices */ 34 static struct sdca_dev *sdca_dev_register(struct device *parent, 35 struct sdca_function_desc *function_desc, 36 struct acpi_table_swft *swft) 37 { 38 struct sdca_dev *sdev; 39 struct auxiliary_device *auxdev; 40 int ret; 41 int rc; 42 43 sdev = kzalloc_obj(*sdev); 44 if (!sdev) 45 return ERR_PTR(-ENOMEM); 46 47 auxdev = &sdev->auxdev; 48 auxdev->name = function_desc->name; 49 auxdev->dev.parent = parent; 50 auxdev->dev.fwnode = function_desc->node; 51 auxdev->dev.release = sdca_dev_release; 52 53 sdev->function.desc = function_desc; 54 sdev->function.fdl_data.swft = swft; 55 56 rc = ida_alloc(&sdca_function_ida, GFP_KERNEL); 57 if (rc < 0) { 58 kfree(sdev); 59 return ERR_PTR(rc); 60 } 61 auxdev->id = rc; 62 63 /* now follow the two-step init/add sequence */ 64 ret = auxiliary_device_init(auxdev); 65 if (ret < 0) { 66 dev_err(parent, "failed to initialize SDCA function dev %s\n", 67 function_desc->name); 68 ida_free(&sdca_function_ida, auxdev->id); 69 kfree(sdev); 70 return ERR_PTR(ret); 71 } 72 73 ret = auxiliary_device_add(auxdev); 74 if (ret < 0) { 75 dev_err(parent, "failed to add SDCA function dev %s\n", 76 sdev->auxdev.name); 77 /* sdev will be freed with the put_device() and .release sequence */ 78 auxiliary_device_uninit(&sdev->auxdev); 79 return ERR_PTR(ret); 80 } 81 82 return sdev; 83 } 84 85 static void sdca_dev_unregister(struct sdca_dev *sdev) 86 { 87 if (!sdev) 88 return; 89 90 auxiliary_device_delete(&sdev->auxdev); 91 auxiliary_device_uninit(&sdev->auxdev); 92 } 93 94 int sdca_dev_register_functions(struct sdw_slave *slave) 95 { 96 struct sdca_device_data *sdca_data = &slave->sdca_data; 97 int i; 98 int ret; 99 100 for (i = 0; i < sdca_data->num_functions; i++) { 101 struct sdca_dev *func_dev; 102 103 func_dev = sdca_dev_register(&slave->dev, 104 &sdca_data->function[i], 105 sdca_data->swft); 106 if (IS_ERR(func_dev)) { 107 ret = PTR_ERR(func_dev); 108 /* 109 * Unregister functions that were successfully 110 * registered before this failure. This also 111 * sets func_dev to NULL so the caller will not 112 * try to unregister them again. 113 */ 114 sdca_dev_unregister_functions(slave); 115 return ret; 116 } 117 118 sdca_data->function[i].func_dev = func_dev; 119 } 120 121 return 0; 122 } 123 EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA"); 124 125 void sdca_dev_unregister_functions(struct sdw_slave *slave) 126 { 127 struct sdca_device_data *sdca_data = &slave->sdca_data; 128 int i; 129 130 for (i = 0; i < sdca_data->num_functions; i++) { 131 if (!sdca_data->function[i].func_dev) 132 continue; 133 134 sdca_dev_unregister(sdca_data->function[i].func_dev); 135 sdca_data->function[i].func_dev = NULL; 136 } 137 } 138 EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA"); 139