Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/sdca/sdca_class.c
122918 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
mutex_init(&drv->init_lock);
209
210
dev_set_drvdata(drv->dev, drv);
211
212
INIT_WORK(&drv->boot_work, class_boot_work);
213
init_completion(&drv->device_attach);
214
215
dev_config->lock_arg = &drv->regmap_lock;
216
217
drv->dev_regmap = devm_regmap_init_sdw(sdw, dev_config);
218
if (IS_ERR(drv->dev_regmap))
219
return dev_err_probe(drv->dev, PTR_ERR(drv->dev_regmap),
220
"failed to create device regmap\n");
221
222
regcache_cache_only(drv->dev_regmap, true);
223
224
pm_runtime_set_autosuspend_delay(dev, 250);
225
pm_runtime_use_autosuspend(dev);
226
pm_runtime_set_active(dev);
227
pm_runtime_get_noresume(dev);
228
229
ret = devm_pm_runtime_enable(dev);
230
if (ret)
231
return ret;
232
233
ret = devm_add_action_or_reset(dev, class_dev_remove, drv);
234
if (ret)
235
return ret;
236
237
queue_work(system_long_wq, &drv->boot_work);
238
239
return 0;
240
}
241
242
static int class_suspend(struct device *dev)
243
{
244
struct sdca_class_drv *drv = dev_get_drvdata(dev);
245
int ret;
246
247
disable_irq(drv->sdw->irq);
248
249
ret = pm_runtime_force_suspend(dev);
250
if (ret) {
251
dev_err(dev, "failed to force suspend: %d\n", ret);
252
return ret;
253
}
254
255
return 0;
256
}
257
258
static int class_resume(struct device *dev)
259
{
260
struct sdca_class_drv *drv = dev_get_drvdata(dev);
261
int ret;
262
263
ret = pm_runtime_force_resume(dev);
264
if (ret) {
265
dev_err(dev, "failed to force resume: %d\n", ret);
266
return ret;
267
}
268
269
enable_irq(drv->sdw->irq);
270
271
return 0;
272
}
273
274
static int class_runtime_suspend(struct device *dev)
275
{
276
struct sdca_class_drv *drv = dev_get_drvdata(dev);
277
278
/*
279
* Whilst the driver doesn't power the chip down here, going into runtime
280
* suspend lets the SoundWire bus power down, which means the driver
281
* can't communicate with the device any more.
282
*/
283
regcache_cache_only(drv->dev_regmap, true);
284
285
return 0;
286
}
287
288
static int class_runtime_resume(struct device *dev)
289
{
290
struct sdca_class_drv *drv = dev_get_drvdata(dev);
291
int ret;
292
293
ret = class_wait_for_attach(drv);
294
if (ret)
295
goto err;
296
297
regcache_mark_dirty(drv->dev_regmap);
298
299
ret = regcache_sync(drv->dev_regmap);
300
if (ret) {
301
dev_err(drv->dev, "failed to restore cache: %d\n", ret);
302
goto err;
303
}
304
305
return 0;
306
307
err:
308
regcache_cache_only(drv->dev_regmap, true);
309
310
return ret;
311
}
312
313
static const struct dev_pm_ops class_pm_ops = {
314
SYSTEM_SLEEP_PM_OPS(class_suspend, class_resume)
315
RUNTIME_PM_OPS(class_runtime_suspend, class_runtime_resume, NULL)
316
};
317
318
static const struct sdw_device_id class_sdw_id[] = {
319
SDW_SLAVE_ENTRY(0x01FA, 0x4245, 0),
320
{}
321
};
322
MODULE_DEVICE_TABLE(sdw, class_sdw_id);
323
324
static struct sdw_driver class_sdw_driver = {
325
.driver = {
326
.name = "sdca_class",
327
.pm = pm_ptr(&class_pm_ops),
328
},
329
330
.probe = class_sdw_probe,
331
.id_table = class_sdw_id,
332
.ops = &class_sdw_ops,
333
};
334
module_sdw_driver(class_sdw_driver);
335
336
MODULE_LICENSE("GPL");
337
MODULE_DESCRIPTION("SDCA Class Driver");
338
MODULE_IMPORT_NS("SND_SOC_SDCA");
339
340