projects/linux·blob master · refs

138 lines · 3.4 KiB

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 */
22static DEFINE_IDA(sdca_function_ida);
23
24static 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 */
34static 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
85static 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
94int 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}
123EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA");
124
125void 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}
138EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");