Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/sdca/sdca_function_device.c
38189 views
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
119