xref: /linux/sound/soc/sdca/sdca_function_device.c (revision 59e6295fac26b8e85c1ea859cdd89fa1e47519d7)
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