Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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{
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
83static void sdca_dev_unregister(struct sdca_dev *sdev)
84{
85 auxiliary_device_delete(&sdev->auxdev);
86 auxiliary_device_uninit(&sdev->auxdev);
87}
88
89int 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}
107EXPORT_SYMBOL_NS(sdca_dev_register_functions, "SND_SOC_SDCA");
108
109void 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}
117EXPORT_SYMBOL_NS(sdca_dev_unregister_functions, "SND_SOC_SDCA");