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
sdca_dev_release(struct device * dev)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 */
sdca_dev_register(struct device * parent,struct sdca_function_desc * function_desc)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
sdca_dev_unregister(struct sdca_dev * sdev)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
sdca_dev_register_functions(struct sdw_slave * slave)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
sdca_dev_unregister_functions(struct sdw_slave * slave)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