138 lines · 3.4 KB · C
RawDownload
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)// Copyright(c) 2024 Intel Corporation. /* * SDCA Function Device management */ #include <linux/acpi.h>#include <linux/module.h>#include <linux/auxiliary_bus.h>#include <linux/soundwire/sdw.h>#include <sound/sdca.h>#include <sound/sdca_function.h>#include "sdca_function_device.h" /* * A SoundWire device can have multiple SDCA functions identified by * their type and ADR. there can be multiple SoundWire devices per * link, or multiple devices spread across multiple links. An IDA is * required to identify each instance. */static DEFINE_IDA(sdca_function_ida); static void sdca_dev_release(struct device *dev){	struct auxiliary_device *auxdev = to_auxiliary_dev(dev);	struct sdca_dev *sdev = auxiliary_dev_to_sdca_dev(auxdev); 	ida_free(&sdca_function_ida, auxdev->id);	kfree(sdev);} /* alloc, init and add link devices */static struct sdca_dev *sdca_dev_register(struct device *parent,					  struct sdca_function_desc *function_desc,					  struct acpi_table_swft *swft){	struct sdca_dev *sdev;	struct auxiliary_device *auxdev;	int ret;	int rc; 	sdev = kzalloc_obj(*sdev);	if (!sdev)		return ERR_PTR(-ENOMEM); 	auxdev = &sdev->auxdev;	auxdev->name = function_desc->name;	auxdev->dev.parent = parent;	auxdev->dev.fwnode = function_desc->node;	auxdev->dev.release = sdca_dev_release; 	sdev->function.desc = function_desc;	sdev->function.fdl_data.swft = swft; 	rc = ida_alloc(&sdca_function_ida, GFP_KERNEL);	if (rc < 0) {		kfree(sdev);		return ERR_PTR(rc);	}	auxdev->id = rc; 	/* now follow the two-step init/add sequence */	ret = auxiliary_device_init(auxdev);	if (ret < 0) {		dev_err(parent, "failed to initialize SDCA function dev %s\n",			function_desc->name);		ida_free(&sdca_function_ida, auxdev->id);		kfree(sdev);		return ERR_PTR(ret);	} 	ret = auxiliary_device_add(auxdev);	if (ret < 0) {		dev_err(parent, "failed to add SDCA function dev %s\n",			sdev->auxdev.name);		/* sdev will be freed with the put_device() and .release sequence */		auxiliary_device_uninit(&sdev->auxdev);		return ERR_PTR(ret);	} 	return sdev;} static void sdca_dev_unregister(struct sdca_dev *sdev){	if (!sdev)		return; 	auxiliary_device_delete(&sdev->auxdev);	auxiliary_device_uninit(&sdev->auxdev);} int sdca_dev_register_functions(struct sdw_slave *slave){	struct sdca_device_data *sdca_data = &slave->sdca_data;	int i;	int ret; 	for (i = 0; i < sdca_data->num_functions; i++) {		struct sdca_dev *func_dev; 		func_dev = sdca_dev_register(&slave->dev,					     &sdca_data->function[i],					     sdca_data->swft);		if (IS_ERR(func_dev)) {			ret = PTR_ERR(func_dev);			/*			 * Unregister functions that were successfully			 * registered before this failure. This also			 * sets func_dev to NULL so the caller will not			 * try to unregister them again.			 */			sdca_dev_unregister_functions(slave);			return ret;		} 		sdca_data->function[i].func_dev = func_dev;	} 	return 0;}EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA"); void sdca_dev_unregister_functions(struct sdw_slave *slave){	struct sdca_device_data *sdca_data = &slave->sdca_data;	int i; 	for (i = 0; i < sdca_data->num_functions; i++) {		if (!sdca_data->function[i].func_dev)			continue; 		sdca_dev_unregister(sdca_data->function[i].func_dev);		sdca_data->function[i].func_dev = NULL;	}}EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");