Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/sdca/sdca_class.c
38189 views
1
// SPDX-License-Identifier: GPL-2.0
2
// Copyright (C) 2025 Cirrus Logic, Inc. and
3
// Cirrus Logic International Semiconductor Ltd.
4
5
/*
6
* The MIPI SDCA specification is available for public downloads at
7
* https://www.mipi.org/mipi-sdca-v1-0-download
8
*/
9
10
#include <linux/device.h>
11
#include <linux/err.h>
12
#include <linux/mod_devicetable.h>
13
#include <linux/module.h>
14
#include <linux/pm.h>
15
#include <linux/pm_runtime.h>
16
#include <linux/regmap.h>
17
#include <linux/soundwire/sdw.h>
18
#include <linux/soundwire/sdw_registers.h>
19
#include <linux/soundwire/sdw_type.h>
20
#include <sound/sdca.h>
21
#include <sound/sdca_function.h>
22
#include <sound/sdca_interrupts.h>
23
#include <sound/sdca_regmap.h>
24
#include "sdca_class.h"
25
26
#define CLASS_SDW_ATTACH_TIMEOUT_MS 5000
27
28
static int class_read_prop(struct sdw_slave *sdw)
29
{
30
struct sdw_slave_prop *prop = &sdw->prop;
31
32
sdw_slave_read_prop(sdw);
33
34
prop->use_domain_irq = true;
35
prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY |
36
SDW_SCP_INT1_IMPL_DEF;
37
38
return 0;
39
}
40
41
static int class_sdw_update_status(struct sdw_slave *sdw, enum sdw_slave_status status)
42
{
43
struct sdca_class_drv *drv = dev_get_drvdata(&sdw->dev);
44
45
switch (status) {
46
case SDW_SLAVE_ATTACHED:
47
dev_dbg(drv->dev, "device attach\n");
48
49
drv->attached = true;
50
51
complete(&drv->device_attach);
52
break;
53
case SDW_SLAVE_UNATTACHED:
54
dev_dbg(drv->dev, "device detach\n");
55
56
drv->attached = false;
57
58
reinit_completion(&drv->device_attach);
59
break;
60
default:
61
break;
62
}
63
64
return 0;
65
}
66
67
static const struct sdw_slave_ops class_sdw_ops = {
68
.read_prop = class_read_prop,
69
.update_status = class_sdw_update_status,
70
};
71
72
static void class_regmap_lock(void *data)
73
{
74
struct mutex *lock = data;
75
76
mutex_lock(lock);
77
}
78
79
static void class_regmap_unlock(void *data)
80
{
81
struct mutex *lock = data;
82
83
mutex_unlock(lock);
84
}
85
86
static int class_wait_for_attach(struct sdca_class_drv *drv)
87
{
88
if (!drv->attached) {
89
unsigned long timeout = msecs_to_jiffies(CLASS_SDW_ATTACH_TIMEOUT_MS);
90
unsigned long time;
91
92
time = wait_for_completion_timeout(&drv->device_attach, timeout);
93
if (!time) {
94
dev_err(drv->dev, "timed out waiting for device re-attach\n");
95
return -ETIMEDOUT;
96
}
97
}
98
99
regcache_cache_only(drv->dev_regmap, false);
100
101
return 0;
102
}
103
104
static bool class_dev_regmap_volatile(struct device *dev, unsigned int reg)
105
{
106
switch (reg) {
107
case SDW_SCP_SDCA_INTMASK1 ... SDW_SCP_SDCA_INTMASK4:
108
return false;
109
default:
110
return true;
111
}
112
}
113
114
static bool class_dev_regmap_precious(struct device *dev, unsigned int reg)
115
{
116
switch (reg) {
117
case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INT4:
118
case SDW_SCP_SDCA_INTMASK1 ... SDW_SCP_SDCA_INTMASK4:
119
return false;
120
default:
121
return true;
122
}
123
}
124
125
static const struct regmap_config class_dev_regmap_config = {
126
.name = "sdca-device",
127
.reg_bits = 32,
128
.val_bits = 8,
129
130
.max_register = SDW_SDCA_MAX_REGISTER,
131
.volatile_reg = class_dev_regmap_volatile,
132
.precious_reg = class_dev_regmap_precious,
133
134
.cache_type = REGCACHE_MAPLE,
135
136
.lock = class_regmap_lock,
137
.unlock = class_regmap_unlock,
138
};
139
140
static void class_boot_work(struct work_struct *work)
141
{
142
struct sdca_class_drv *drv = container_of(work,
143
struct sdca_class_drv,
144
boot_work);
145
int ret;
146
147
ret = class_wait_for_attach(drv);
148
if (ret)
149
goto err;
150
151
drv->irq_info = sdca_irq_allocate(drv->dev, drv->dev_regmap,
152
drv->sdw->irq);
153
if (IS_ERR(drv->irq_info))
154
goto err;
155
156
ret = sdca_dev_register_functions(drv->sdw);
157
if (ret)
158
goto err;
159
160
dev_dbg(drv->dev, "boot work complete\n");
161
162
pm_runtime_mark_last_busy(drv->dev);
163
pm_runtime_put_autosuspend(drv->dev);
164
165
return;
166
167
err:
168
pm_runtime_put_sync(drv->dev);
169
}
170
171
static void class_dev_remove(void *data)
172
{
173
struct sdca_class_drv *drv = data;
174
175
cancel_work_sync(&drv->boot_work);
176
177
sdca_dev_unregister_functions(drv->sdw);
178
}
179
180
static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id)
181
{
182
struct device *dev = &sdw->dev;
183
struct sdca_device_data *data = &sdw->sdca_data;
184
struct regmap_config *dev_config;
185
struct sdca_class_drv *drv;
186
int ret;
187
188
sdca_lookup_swft(sdw);
189
190
drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
191
if (!drv)
192
return -ENOMEM;
193
194
dev_config = devm_kmemdup(dev, &class_dev_regmap_config,
195
sizeof(*dev_config), GFP_KERNEL);
196
if (!dev_config)
197
return -ENOMEM;
198
199
drv->functions = devm_kcalloc(dev, data->num_functions,
200
sizeof(*drv->functions),
201
GFP_KERNEL);
202
if (!drv->functions)
203
return -ENOMEM;
204
205
drv->dev = dev;
206
drv->sdw = sdw;
207
mutex_init(&drv->regmap_lock);
208
209
dev_set_drvdata(drv->dev, drv);
210
211
INIT_WORK(&drv->boot_work, class_boot_work);
212
init_completion(&drv->device_attach);
213
214
dev_config->lock_arg = &drv->regmap_lock;
215
216
drv->dev_regmap = devm_regmap_init_sdw(sdw, dev_config);
217
if (IS_ERR(drv->dev_regmap))
218
return dev_err_probe(drv->dev, PTR_ERR(drv->dev_regmap),
219
"failed to create device regmap\n");
220
221
regcache_cache_only(drv->dev_regmap, true);
222
223
pm_runtime_set_autosuspend_delay(dev, 250);
224
pm_runtime_use_autosuspend(dev);
225
pm_runtime_set_active(dev);
226
pm_runtime_get_noresume(dev);
227
228
ret = devm_pm_runtime_enable(dev);
229
if (ret)
230
return ret;
231
232
ret = devm_add_action_or_reset(dev, class_dev_remove, drv);
233
if (ret)
234
return ret;
235
236
queue_work(system_long_wq, &drv->boot_work);
237
238
return 0;
239
}
240
241
static int class_runtime_suspend(struct device *dev)
242
{
243
struct sdca_class_drv *drv = dev_get_drvdata(dev);
244
245
/*
246
* Whilst the driver doesn't power the chip down here, going into runtime
247
* suspend lets the SoundWire bus power down, which means the driver
248
* can't communicate with the device any more.
249
*/
250
regcache_cache_only(drv->dev_regmap, true);
251
252
return 0;
253
}
254
255
static int class_runtime_resume(struct device *dev)
256
{
257
struct sdca_class_drv *drv = dev_get_drvdata(dev);
258
int ret;
259
260
ret = class_wait_for_attach(drv);
261
if (ret)
262
goto err;
263
264
regcache_mark_dirty(drv->dev_regmap);
265
266
ret = regcache_sync(drv->dev_regmap);
267
if (ret) {
268
dev_err(drv->dev, "failed to restore cache: %d\n", ret);
269
goto err;
270
}
271
272
return 0;
273
274
err:
275
regcache_cache_only(drv->dev_regmap, true);
276
277
return ret;
278
}
279
280
static const struct dev_pm_ops class_pm_ops = {
281
RUNTIME_PM_OPS(class_runtime_suspend, class_runtime_resume, NULL)
282
};
283
284
static const struct sdw_device_id class_sdw_id[] = {
285
SDW_SLAVE_ENTRY(0x01FA, 0x4245, 0),
286
{}
287
};
288
MODULE_DEVICE_TABLE(sdw, class_sdw_id);
289
290
static struct sdw_driver class_sdw_driver = {
291
.driver = {
292
.name = "sdca_class",
293
.pm = pm_ptr(&class_pm_ops),
294
},
295
296
.probe = class_sdw_probe,
297
.id_table = class_sdw_id,
298
.ops = &class_sdw_ops,
299
};
300
module_sdw_driver(class_sdw_driver);
301
302
MODULE_LICENSE("GPL");
303
MODULE_DESCRIPTION("SDCA Class Driver");
304
MODULE_IMPORT_NS("SND_SOC_SDCA");
305
306