xref: /linux/sound/soc/sdca/sdca_function_device.c (revision 84318277d6334c6981ab326d4acc87c6a6ddc9b8)
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(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 
83 static void sdca_dev_unregister(struct sdca_dev *sdev)
84 {
85 	auxiliary_device_delete(&sdev->auxdev);
86 	auxiliary_device_uninit(&sdev->auxdev);
87 }
88 
89 int 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 }
107 EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA");
108 
109 void 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 }
117 EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");
118