xref: /linux/sound/soc/sdca/sdca_function_device.c (revision 84318277d6334c6981ab326d4acc87c6a6ddc9b8)
14496d1c6SPierre-Louis Bossart // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
24496d1c6SPierre-Louis Bossart // Copyright(c) 2024 Intel Corporation.
34496d1c6SPierre-Louis Bossart 
44496d1c6SPierre-Louis Bossart /*
54496d1c6SPierre-Louis Bossart  * SDCA Function Device management
64496d1c6SPierre-Louis Bossart  */
74496d1c6SPierre-Louis Bossart 
84496d1c6SPierre-Louis Bossart #include <linux/acpi.h>
94496d1c6SPierre-Louis Bossart #include <linux/module.h>
104496d1c6SPierre-Louis Bossart #include <linux/auxiliary_bus.h>
114496d1c6SPierre-Louis Bossart #include <linux/soundwire/sdw.h>
124496d1c6SPierre-Louis Bossart #include <sound/sdca.h>
134496d1c6SPierre-Louis Bossart #include <sound/sdca_function.h>
144496d1c6SPierre-Louis Bossart #include "sdca_function_device.h"
154496d1c6SPierre-Louis Bossart 
164496d1c6SPierre-Louis Bossart /*
174496d1c6SPierre-Louis Bossart  * A SoundWire device can have multiple SDCA functions identified by
184496d1c6SPierre-Louis Bossart  * their type and ADR. there can be multiple SoundWire devices per
194496d1c6SPierre-Louis Bossart  * link, or multiple devices spread across multiple links. An IDA is
204496d1c6SPierre-Louis Bossart  * required to identify each instance.
214496d1c6SPierre-Louis Bossart  */
224496d1c6SPierre-Louis Bossart static DEFINE_IDA(sdca_function_ida);
234496d1c6SPierre-Louis Bossart 
244496d1c6SPierre-Louis Bossart static void sdca_dev_release(struct device *dev)
254496d1c6SPierre-Louis Bossart {
264496d1c6SPierre-Louis Bossart 	struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
274496d1c6SPierre-Louis Bossart 	struct sdca_dev *sdev = auxiliary_dev_to_sdca_dev(auxdev);
284496d1c6SPierre-Louis Bossart 
294496d1c6SPierre-Louis Bossart 	ida_free(&sdca_function_ida, auxdev->id);
304496d1c6SPierre-Louis Bossart 	kfree(sdev);
314496d1c6SPierre-Louis Bossart }
324496d1c6SPierre-Louis Bossart 
334496d1c6SPierre-Louis Bossart /* alloc, init and add link devices */
344496d1c6SPierre-Louis Bossart static struct sdca_dev *sdca_dev_register(struct device *parent,
354496d1c6SPierre-Louis Bossart 					  struct sdca_function_desc *function_desc)
364496d1c6SPierre-Louis Bossart {
374496d1c6SPierre-Louis Bossart 	struct sdca_dev *sdev;
384496d1c6SPierre-Louis Bossart 	struct auxiliary_device *auxdev;
394496d1c6SPierre-Louis Bossart 	int ret;
404496d1c6SPierre-Louis Bossart 	int rc;
414496d1c6SPierre-Louis Bossart 
424496d1c6SPierre-Louis Bossart 	sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
434496d1c6SPierre-Louis Bossart 	if (!sdev)
444496d1c6SPierre-Louis Bossart 		return ERR_PTR(-ENOMEM);
454496d1c6SPierre-Louis Bossart 
464496d1c6SPierre-Louis Bossart 	auxdev = &sdev->auxdev;
474496d1c6SPierre-Louis Bossart 	auxdev->name = function_desc->name;
484496d1c6SPierre-Louis Bossart 	auxdev->dev.parent = parent;
494496d1c6SPierre-Louis Bossart 	auxdev->dev.fwnode = function_desc->node;
504496d1c6SPierre-Louis Bossart 	auxdev->dev.release = sdca_dev_release;
514496d1c6SPierre-Louis Bossart 
524496d1c6SPierre-Louis Bossart 	sdev->function.desc = function_desc;
534496d1c6SPierre-Louis Bossart 
544496d1c6SPierre-Louis Bossart 	rc = ida_alloc(&sdca_function_ida, GFP_KERNEL);
554496d1c6SPierre-Louis Bossart 	if (rc < 0) {
564496d1c6SPierre-Louis Bossart 		kfree(sdev);
574496d1c6SPierre-Louis Bossart 		return ERR_PTR(rc);
584496d1c6SPierre-Louis Bossart 	}
594496d1c6SPierre-Louis Bossart 	auxdev->id = rc;
604496d1c6SPierre-Louis Bossart 
614496d1c6SPierre-Louis Bossart 	/* now follow the two-step init/add sequence */
624496d1c6SPierre-Louis Bossart 	ret = auxiliary_device_init(auxdev);
634496d1c6SPierre-Louis Bossart 	if (ret < 0) {
644496d1c6SPierre-Louis Bossart 		dev_err(parent, "failed to initialize SDCA function dev %s\n",
654496d1c6SPierre-Louis Bossart 			function_desc->name);
664496d1c6SPierre-Louis Bossart 		ida_free(&sdca_function_ida, auxdev->id);
674496d1c6SPierre-Louis Bossart 		kfree(sdev);
684496d1c6SPierre-Louis Bossart 		return ERR_PTR(ret);
694496d1c6SPierre-Louis Bossart 	}
704496d1c6SPierre-Louis Bossart 
714496d1c6SPierre-Louis Bossart 	ret = auxiliary_device_add(auxdev);
724496d1c6SPierre-Louis Bossart 	if (ret < 0) {
734496d1c6SPierre-Louis Bossart 		dev_err(parent, "failed to add SDCA function dev %s\n",
744496d1c6SPierre-Louis Bossart 			sdev->auxdev.name);
754496d1c6SPierre-Louis Bossart 		/* sdev will be freed with the put_device() and .release sequence */
764496d1c6SPierre-Louis Bossart 		auxiliary_device_uninit(&sdev->auxdev);
774496d1c6SPierre-Louis Bossart 		return ERR_PTR(ret);
784496d1c6SPierre-Louis Bossart 	}
794496d1c6SPierre-Louis Bossart 
804496d1c6SPierre-Louis Bossart 	return sdev;
814496d1c6SPierre-Louis Bossart }
824496d1c6SPierre-Louis Bossart 
834496d1c6SPierre-Louis Bossart static void sdca_dev_unregister(struct sdca_dev *sdev)
844496d1c6SPierre-Louis Bossart {
854496d1c6SPierre-Louis Bossart 	auxiliary_device_delete(&sdev->auxdev);
864496d1c6SPierre-Louis Bossart 	auxiliary_device_uninit(&sdev->auxdev);
874496d1c6SPierre-Louis Bossart }
884496d1c6SPierre-Louis Bossart 
894496d1c6SPierre-Louis Bossart int sdca_dev_register_functions(struct sdw_slave *slave)
904496d1c6SPierre-Louis Bossart {
914496d1c6SPierre-Louis Bossart 	struct sdca_device_data *sdca_data = &slave->sdca_data;
924496d1c6SPierre-Louis Bossart 	int i;
934496d1c6SPierre-Louis Bossart 
944496d1c6SPierre-Louis Bossart 	for (i = 0; i < sdca_data->num_functions; i++) {
954496d1c6SPierre-Louis Bossart 		struct sdca_dev *func_dev;
964496d1c6SPierre-Louis Bossart 
974496d1c6SPierre-Louis Bossart 		func_dev = sdca_dev_register(&slave->dev,
984496d1c6SPierre-Louis Bossart 					     &sdca_data->function[i]);
99*80339b38SDan Carpenter 		if (IS_ERR(func_dev))
100*80339b38SDan Carpenter 			return PTR_ERR(func_dev);
1014496d1c6SPierre-Louis Bossart 
1024496d1c6SPierre-Louis Bossart 		sdca_data->function[i].func_dev = func_dev;
1034496d1c6SPierre-Louis Bossart 	}
1044496d1c6SPierre-Louis Bossart 
1054496d1c6SPierre-Louis Bossart 	return 0;
1064496d1c6SPierre-Louis Bossart }
1074496d1c6SPierre-Louis Bossart EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA");
1084496d1c6SPierre-Louis Bossart 
1094496d1c6SPierre-Louis Bossart void sdca_dev_unregister_functions(struct sdw_slave *slave)
1104496d1c6SPierre-Louis Bossart {
1114496d1c6SPierre-Louis Bossart 	struct sdca_device_data *sdca_data = &slave->sdca_data;
1124496d1c6SPierre-Louis Bossart 	int i;
1134496d1c6SPierre-Louis Bossart 
1144496d1c6SPierre-Louis Bossart 	for (i = 0; i < sdca_data->num_functions; i++)
1154496d1c6SPierre-Louis Bossart 		sdca_dev_unregister(sdca_data->function[i].func_dev);
1164496d1c6SPierre-Louis Bossart }
1174496d1c6SPierre-Louis Bossart EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");
118