xref: /linux/sound/soc/sdca/sdca_function_device.c (revision e4c60a1d4b6ccc66aefb3789cd908d4f9482eefd)
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_obj(*sdev);
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 	if (!sdev)
86 		return;
87 
88 	auxiliary_device_delete(&sdev->auxdev);
89 	auxiliary_device_uninit(&sdev->auxdev);
90 }
91 
92 int sdca_dev_register_functions(struct sdw_slave *slave)
93 {
94 	struct sdca_device_data *sdca_data = &slave->sdca_data;
95 	int i;
96 	int ret;
97 
98 	for (i = 0; i < sdca_data->num_functions; i++) {
99 		struct sdca_dev *func_dev;
100 
101 		func_dev = sdca_dev_register(&slave->dev,
102 					     &sdca_data->function[i]);
103 		if (IS_ERR(func_dev)) {
104 			ret = PTR_ERR(func_dev);
105 			/*
106 			 * Unregister functions that were successfully
107 			 * registered before this failure. This also
108 			 * sets func_dev to NULL so the caller will not
109 			 * try to unregister them again.
110 			 */
111 			sdca_dev_unregister_functions(slave);
112 			return ret;
113 		}
114 
115 		sdca_data->function[i].func_dev = func_dev;
116 	}
117 
118 	return 0;
119 }
120 EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA");
121 
122 void sdca_dev_unregister_functions(struct sdw_slave *slave)
123 {
124 	struct sdca_device_data *sdca_data = &slave->sdca_data;
125 	int i;
126 
127 	for (i = 0; i < sdca_data->num_functions; i++) {
128 		if (!sdca_data->function[i].func_dev)
129 			continue;
130 
131 		sdca_dev_unregister(sdca_data->function[i].func_dev);
132 		sdca_data->function[i].func_dev = NULL;
133 	}
134 }
135 EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");
136