at master 3.0 kB view raw
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 */ 22static DEFINE_IDA(sdca_function_ida); 23 24static 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 */ 34static 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 83static void sdca_dev_unregister(struct sdca_dev *sdev) 84{ 85 auxiliary_device_delete(&sdev->auxdev); 86 auxiliary_device_uninit(&sdev->auxdev); 87} 88 89int 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} 107EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA"); 108 109void 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} 117EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");