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 { 37 struct sdca_dev *sdev; 38 struct auxiliary_device *auxdev; 39 int ret; 40 int rc; 41 42 sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); 43 if (!sdev) 44 return ERR_PTR(-ENOMEM); 45 46 auxdev = &sdev->auxdev; 47 auxdev->name = function_desc->name; 48 auxdev->dev.parent = parent; 49 auxdev->dev.fwnode = function_desc->node; 50 auxdev->dev.release = sdca_dev_release; 51 52 sdev->function.desc = function_desc; 53 54 rc = ida_alloc(&sdca_function_ida, GFP_KERNEL); 55 if (rc < 0) { 56 kfree(sdev); 57 return ERR_PTR(rc); 58 } 59 auxdev->id = rc; 60 61 /* now follow the two-step init/add sequence */ 62 ret = auxiliary_device_init(auxdev); 63 if (ret < 0) { 64 dev_err(parent, "failed to initialize SDCA function dev %s\n", 65 function_desc->name); 66 ida_free(&sdca_function_ida, auxdev->id); 67 kfree(sdev); 68 return ERR_PTR(ret); 69 } 70 71 ret = auxiliary_device_add(auxdev); 72 if (ret < 0) { 73 dev_err(parent, "failed to add SDCA function dev %s\n", 74 sdev->auxdev.name); 75 /* sdev will be freed with the put_device() and .release sequence */ 76 auxiliary_device_uninit(&sdev->auxdev); 77 return ERR_PTR(ret); 78 } 79 80 return sdev; 81 } 82 83 static void sdca_dev_unregister(struct sdca_dev *sdev) 84 { 85 auxiliary_device_delete(&sdev->auxdev); 86 auxiliary_device_uninit(&sdev->auxdev); 87 } 88 89 int sdca_dev_register_functions(struct sdw_slave *slave) 90 { 91 struct sdca_device_data *sdca_data = &slave->sdca_data; 92 int i; 93 94 for (i = 0; i < sdca_data->num_functions; i++) { 95 struct sdca_dev *func_dev; 96 97 func_dev = sdca_dev_register(&slave->dev, 98 &sdca_data->function[i]); 99 if (IS_ERR(func_dev)) 100 return PTR_ERR(func_dev); 101 102 sdca_data->function[i].func_dev = func_dev; 103 } 104 105 return 0; 106 } 107 EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA"); 108 109 void sdca_dev_unregister_functions(struct sdw_slave *slave) 110 { 111 struct sdca_device_data *sdca_data = &slave->sdca_data; 112 int i; 113 114 for (i = 0; i < sdca_data->num_functions; i++) 115 sdca_dev_unregister(sdca_data->function[i].func_dev); 116 } 117 EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA"); 118