Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/sdca/sdca_fdl.c
51610 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/acpi.h>
11
#include <linux/device.h>
12
#include <linux/dev_printk.h>
13
#include <linux/dmi.h>
14
#include <linux/firmware.h>
15
#include <linux/module.h>
16
#include <linux/pci.h>
17
#include <linux/pm_runtime.h>
18
#include <linux/regmap.h>
19
#include <linux/sprintf.h>
20
#include <linux/soundwire/sdw.h>
21
#include <linux/soundwire/sdw_registers.h>
22
#include <sound/sdca.h>
23
#include <sound/sdca_fdl.h>
24
#include <sound/sdca_function.h>
25
#include <sound/sdca_interrupts.h>
26
#include <sound/sdca_ump.h>
27
28
/**
29
* sdca_reset_function - send an SDCA function reset
30
* @dev: Device pointer for error messages.
31
* @function: Pointer to the SDCA Function.
32
* @regmap: Pointer to the SDCA Function regmap.
33
*
34
* Return: Zero on success or a negative error code.
35
*/
36
int sdca_reset_function(struct device *dev, struct sdca_function_data *function,
37
struct regmap *regmap)
38
{
39
unsigned int reg = SDW_SDCA_CTL(function->desc->adr,
40
SDCA_ENTITY_TYPE_ENTITY_0,
41
SDCA_CTL_ENTITY_0_FUNCTION_ACTION, 0);
42
unsigned int val, poll_us;
43
int ret;
44
45
ret = regmap_write(regmap, reg, SDCA_CTL_ENTITY_0_RESET_FUNCTION_NOW);
46
if (ret) // Allowed for function reset to not be implemented
47
return 0;
48
49
if (!function->reset_max_delay) {
50
dev_err(dev, "No reset delay specified in DisCo\n");
51
return -EINVAL;
52
}
53
54
/*
55
* Poll up to 16 times but no more than once per ms, these are just
56
* arbitrarily selected values, so may be fine tuned in future.
57
*/
58
poll_us = umin(function->reset_max_delay >> 4, 1000);
59
60
ret = regmap_read_poll_timeout(regmap, reg, val, !val, poll_us,
61
function->reset_max_delay);
62
if (ret) {
63
dev_err(dev, "Failed waiting for function reset: %d\n", ret);
64
return ret;
65
}
66
67
return 0;
68
}
69
EXPORT_SYMBOL_NS(sdca_reset_function, "SND_SOC_SDCA");
70
71
/**
72
* sdca_fdl_sync - wait for a function to finish FDL
73
* @dev: Device pointer for error messages.
74
* @function: Pointer to the SDCA Function.
75
* @info: Pointer to the SDCA interrupt info for this device.
76
*
77
* Return: Zero on success or a negative error code.
78
*/
79
int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function,
80
struct sdca_interrupt_info *info)
81
{
82
static const int fdl_retries = 6;
83
unsigned long begin_timeout = msecs_to_jiffies(100);
84
unsigned long done_timeout = msecs_to_jiffies(4000);
85
int nfdl;
86
int i, j;
87
88
for (i = 0; i < fdl_retries; i++) {
89
nfdl = 0;
90
91
for (j = 0; j < SDCA_MAX_INTERRUPTS; j++) {
92
struct sdca_interrupt *interrupt = &info->irqs[j];
93
struct fdl_state *fdl_state;
94
unsigned long time;
95
96
if (interrupt->function != function ||
97
!interrupt->entity || !interrupt->control ||
98
interrupt->entity->type != SDCA_ENTITY_TYPE_XU ||
99
interrupt->control->sel != SDCA_CTL_XU_FDL_CURRENTOWNER)
100
continue;
101
102
fdl_state = interrupt->priv;
103
nfdl++;
104
105
/*
106
* Looking for timeout without any new FDL requests
107
* to imply the device has completed initial
108
* firmware setup. Alas the specification doesn't
109
* have any mechanism to detect this.
110
*/
111
time = wait_for_completion_timeout(&fdl_state->begin,
112
begin_timeout);
113
if (!time) {
114
dev_dbg(dev, "no new FDL starts\n");
115
nfdl--;
116
continue;
117
}
118
119
time = wait_for_completion_timeout(&fdl_state->done,
120
done_timeout);
121
if (!time) {
122
dev_err(dev, "timed out waiting for FDL to complete\n");
123
goto error;
124
}
125
}
126
127
if (!nfdl)
128
return 0;
129
}
130
131
dev_err(dev, "too many FDL requests\n");
132
133
error:
134
for (j = 0; j < SDCA_MAX_INTERRUPTS; j++) {
135
struct sdca_interrupt *interrupt = &info->irqs[j];
136
struct fdl_state *fdl_state;
137
138
if (interrupt->function != function ||
139
!interrupt->entity || !interrupt->control ||
140
interrupt->entity->type != SDCA_ENTITY_TYPE_XU ||
141
interrupt->control->sel != SDCA_CTL_XU_FDL_CURRENTOWNER)
142
continue;
143
144
disable_irq(interrupt->irq);
145
146
fdl_state = interrupt->priv;
147
148
sdca_ump_cancel_timeout(&fdl_state->timeout);
149
}
150
151
return -ETIMEDOUT;
152
}
153
EXPORT_SYMBOL_NS_GPL(sdca_fdl_sync, "SND_SOC_SDCA");
154
155
static char *fdl_get_sku_filename(struct device *dev,
156
struct sdca_fdl_file *fdl_file)
157
{
158
struct device *parent = dev;
159
const char *product_vendor;
160
const char *product_sku;
161
162
/*
163
* Try to find pci_dev manually because the card may not be ready to be
164
* used for snd_soc_card_get_pci_ssid yet
165
*/
166
while (parent) {
167
if (dev_is_pci(parent)) {
168
struct pci_dev *pci_dev = to_pci_dev(parent);
169
170
return kasprintf(GFP_KERNEL, "sdca/%x/%x/%x/%x.bin",
171
fdl_file->vendor_id,
172
pci_dev->subsystem_vendor,
173
pci_dev->subsystem_device,
174
fdl_file->file_id);
175
} else {
176
parent = parent->parent;
177
}
178
}
179
180
product_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
181
if (!product_vendor || !strcmp(product_vendor, "Default string"))
182
product_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
183
if (!product_vendor || !strcmp(product_vendor, "Default string"))
184
product_vendor = dmi_get_system_info(DMI_CHASSIS_VENDOR);
185
if (!product_vendor)
186
product_vendor = "unknown";
187
188
product_sku = dmi_get_system_info(DMI_PRODUCT_SKU);
189
if (!product_sku || !strcmp(product_sku, "Default string"))
190
product_sku = dmi_get_system_info(DMI_PRODUCT_NAME);
191
if (!product_sku)
192
product_sku = "unknown";
193
194
return kasprintf(GFP_KERNEL, "sdca/%x/%s/%s/%x.bin", fdl_file->vendor_id,
195
product_vendor, product_sku, fdl_file->file_id);
196
}
197
198
static int fdl_load_file(struct sdca_interrupt *interrupt,
199
struct sdca_fdl_set *set, int file_index)
200
{
201
struct device *dev = interrupt->dev;
202
struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data;
203
const struct firmware *firmware = NULL;
204
struct acpi_sw_file *swf = NULL, *tmp;
205
struct sdca_fdl_file *fdl_file;
206
char *disk_filename;
207
int ret;
208
int i;
209
210
if (!set) {
211
dev_err(dev, "request to load SWF with no set\n");
212
return -EINVAL;
213
}
214
215
fdl_file = &set->files[file_index];
216
217
if (fdl_data->swft) {
218
tmp = fdl_data->swft->files;
219
for (i = 0; i < fdl_data->swft->header.length; i += tmp->file_length,
220
tmp = ACPI_ADD_PTR(struct acpi_sw_file, tmp, tmp->file_length)) {
221
if (tmp->vendor_id == fdl_file->vendor_id &&
222
tmp->file_id == fdl_file->file_id) {
223
dev_dbg(dev, "located SWF in ACPI: %x-%x-%x\n",
224
tmp->vendor_id, tmp->file_id,
225
tmp->file_version);
226
swf = tmp;
227
break;
228
}
229
}
230
}
231
232
disk_filename = fdl_get_sku_filename(dev, fdl_file);
233
if (!disk_filename)
234
return -ENOMEM;
235
236
dev_dbg(dev, "FDL disk filename: %s\n", disk_filename);
237
238
ret = firmware_request_nowarn(&firmware, disk_filename, dev);
239
kfree(disk_filename);
240
if (ret) {
241
disk_filename = kasprintf(GFP_KERNEL, "sdca/%x/%x.bin",
242
fdl_file->vendor_id, fdl_file->file_id);
243
if (!disk_filename)
244
return -ENOMEM;
245
246
dev_dbg(dev, "FDL disk filename: %s\n", disk_filename);
247
248
ret = firmware_request_nowarn(&firmware, disk_filename, dev);
249
kfree(disk_filename);
250
}
251
252
if (!ret) {
253
tmp = (struct acpi_sw_file *)&firmware->data[0];
254
255
if (firmware->size < sizeof(*tmp) ||
256
tmp->file_length != firmware->size) {
257
dev_err(dev, "bad disk SWF size\n");
258
} else if (!swf || swf->file_version <= tmp->file_version) {
259
dev_dbg(dev, "using SWF from disk\n");
260
swf = tmp;
261
}
262
}
263
264
if (!swf) {
265
dev_err(dev, "failed to locate SWF\n");
266
return -ENOENT;
267
}
268
269
dev_info(dev, "loading SWF: %x-%x-%x\n",
270
swf->vendor_id, swf->file_id, swf->file_version);
271
272
ret = sdca_ump_write_message(dev, interrupt->device_regmap,
273
interrupt->function_regmap,
274
interrupt->function, interrupt->entity,
275
SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset,
276
SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data,
277
swf->file_length - offsetof(struct acpi_sw_file, data));
278
release_firmware(firmware);
279
return ret;
280
}
281
282
static struct sdca_fdl_set *fdl_get_set(struct sdca_interrupt *interrupt)
283
{
284
struct device *dev = interrupt->dev;
285
struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data;
286
struct sdca_entity *xu = interrupt->entity;
287
struct sdca_control_range *range;
288
unsigned int val;
289
int i, ret;
290
291
ret = regmap_read(interrupt->function_regmap,
292
SDW_SDCA_CTL(interrupt->function->desc->adr, xu->id,
293
SDCA_CTL_XU_FDL_SET_INDEX, 0),
294
&val);
295
if (ret < 0) {
296
dev_err(dev, "failed to read FDL set index: %d\n", ret);
297
return NULL;
298
}
299
300
range = sdca_selector_find_range(dev, xu, SDCA_CTL_XU_FDL_SET_INDEX,
301
SDCA_FDL_SET_INDEX_NCOLS, 0);
302
303
val = sdca_range_search(range, SDCA_FDL_SET_INDEX_SET_NUMBER,
304
val, SDCA_FDL_SET_INDEX_FILE_SET_ID);
305
306
for (i = 0; i < fdl_data->num_sets; i++) {
307
if (fdl_data->sets[i].id == val)
308
return &fdl_data->sets[i];
309
}
310
311
dev_err(dev, "invalid fileset id: %d\n", val);
312
return NULL;
313
}
314
315
static void fdl_end(struct sdca_interrupt *interrupt)
316
{
317
struct fdl_state *fdl_state = interrupt->priv;
318
319
if (!fdl_state->set)
320
return;
321
322
fdl_state->set = NULL;
323
324
pm_runtime_put(interrupt->dev);
325
complete(&fdl_state->done);
326
327
dev_dbg(interrupt->dev, "completed FDL process\n");
328
}
329
330
static void sdca_fdl_timeout_work(struct work_struct *work)
331
{
332
struct fdl_state *fdl_state = container_of(work, struct fdl_state,
333
timeout.work);
334
struct sdca_interrupt *interrupt = fdl_state->interrupt;
335
struct device *dev = interrupt->dev;
336
337
dev_err(dev, "FDL transaction timed out\n");
338
339
guard(mutex)(&fdl_state->lock);
340
341
fdl_end(interrupt);
342
sdca_reset_function(dev, interrupt->function, interrupt->function_regmap);
343
}
344
345
static int fdl_status_process(struct sdca_interrupt *interrupt, unsigned int status)
346
{
347
struct fdl_state *fdl_state = interrupt->priv;
348
int ret;
349
350
switch (status) {
351
case SDCA_CTL_XU_FDLD_NEEDS_SET:
352
dev_dbg(interrupt->dev, "starting FDL process...\n");
353
354
pm_runtime_get(interrupt->dev);
355
complete(&fdl_state->begin);
356
357
fdl_state->file_index = 0;
358
fdl_state->set = fdl_get_set(interrupt);
359
fallthrough;
360
case SDCA_CTL_XU_FDLD_MORE_FILES_OK:
361
ret = fdl_load_file(interrupt, fdl_state->set, fdl_state->file_index);
362
if (ret) {
363
fdl_end(interrupt);
364
return SDCA_CTL_XU_FDLH_REQ_ABORT;
365
}
366
367
return SDCA_CTL_XU_FDLH_FILE_AVAILABLE;
368
case SDCA_CTL_XU_FDLD_FILE_OK:
369
if (!fdl_state->set) {
370
fdl_end(interrupt);
371
return SDCA_CTL_XU_FDLH_REQ_ABORT;
372
}
373
374
fdl_state->file_index++;
375
376
if (fdl_state->file_index < fdl_state->set->num_files)
377
return SDCA_CTL_XU_FDLH_MORE_FILES;
378
fallthrough;
379
case SDCA_CTL_XU_FDLD_COMPLETE:
380
fdl_end(interrupt);
381
return SDCA_CTL_XU_FDLH_COMPLETE;
382
default:
383
fdl_end(interrupt);
384
385
if (status & SDCA_CTL_XU_FDLD_REQ_RESET)
386
return SDCA_CTL_XU_FDLH_RESET_ACK;
387
else if (status & SDCA_CTL_XU_FDLD_REQ_ABORT)
388
return SDCA_CTL_XU_FDLH_COMPLETE;
389
390
dev_err(interrupt->dev, "invalid FDL status: %x\n", status);
391
return -EINVAL;
392
}
393
}
394
395
/**
396
* sdca_fdl_process - Process the FDL state machine
397
* @interrupt: SDCA interrupt structure
398
*
399
* Based on section 13.2.5 Flow Diagram for File Download, Host side.
400
*
401
* Return: Zero on success or a negative error code.
402
*/
403
int sdca_fdl_process(struct sdca_interrupt *interrupt)
404
{
405
struct device *dev = interrupt->dev;
406
struct sdca_entity_xu *xu = &interrupt->entity->xu;
407
struct fdl_state *fdl_state = interrupt->priv;
408
unsigned int reg, status;
409
int response, ret;
410
411
ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap,
412
interrupt->function, interrupt->entity,
413
interrupt->control);
414
if (ret)
415
goto reset_function;
416
417
sdca_ump_cancel_timeout(&fdl_state->timeout);
418
419
scoped_guard(mutex, &fdl_state->lock) {
420
reg = SDW_SDCA_CTL(interrupt->function->desc->adr,
421
interrupt->entity->id, SDCA_CTL_XU_FDL_STATUS, 0);
422
ret = regmap_read(interrupt->function_regmap, reg, &status);
423
if (ret < 0) {
424
dev_err(dev, "failed to read FDL status: %d\n", ret);
425
return ret;
426
}
427
428
dev_dbg(dev, "FDL status: %#x\n", status);
429
430
ret = fdl_status_process(interrupt, status);
431
if (ret < 0)
432
goto reset_function;
433
434
response = ret;
435
436
dev_dbg(dev, "FDL response: %#x\n", response);
437
438
ret = regmap_write(interrupt->function_regmap, reg,
439
response | (status & ~SDCA_CTL_XU_FDLH_MASK));
440
if (ret < 0) {
441
dev_err(dev, "failed to set FDL status signal: %d\n", ret);
442
return ret;
443
}
444
445
ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap,
446
interrupt->function,
447
interrupt->entity,
448
interrupt->control);
449
if (ret)
450
return ret;
451
452
switch (response) {
453
case SDCA_CTL_XU_FDLH_RESET_ACK:
454
dev_dbg(dev, "FDL request reset\n");
455
456
switch (xu->reset_mechanism) {
457
default:
458
dev_warn(dev, "Requested reset mechanism not implemented\n");
459
fallthrough;
460
case SDCA_XU_RESET_FUNCTION:
461
goto reset_function;
462
}
463
case SDCA_CTL_XU_FDLH_COMPLETE:
464
if (status & SDCA_CTL_XU_FDLD_REQ_ABORT ||
465
status == SDCA_CTL_XU_FDLD_COMPLETE)
466
return 0;
467
fallthrough;
468
default:
469
sdca_ump_schedule_timeout(&fdl_state->timeout, xu->max_delay);
470
return 0;
471
}
472
}
473
474
reset_function:
475
sdca_reset_function(dev, interrupt->function, interrupt->function_regmap);
476
477
return ret;
478
}
479
EXPORT_SYMBOL_NS_GPL(sdca_fdl_process, "SND_SOC_SDCA");
480
481
/**
482
* sdca_fdl_alloc_state - allocate state for an FDL interrupt
483
* @interrupt: SDCA interrupt structure.
484
*
485
* Return: Zero on success or a negative error code.
486
*/
487
int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt)
488
{
489
struct device *dev = interrupt->dev;
490
struct fdl_state *fdl_state;
491
492
fdl_state = devm_kzalloc(dev, sizeof(*fdl_state), GFP_KERNEL);
493
if (!fdl_state)
494
return -ENOMEM;
495
496
INIT_DELAYED_WORK(&fdl_state->timeout, sdca_fdl_timeout_work);
497
init_completion(&fdl_state->begin);
498
init_completion(&fdl_state->done);
499
mutex_init(&fdl_state->lock);
500
fdl_state->interrupt = interrupt;
501
502
interrupt->priv = fdl_state;
503
504
return 0;
505
}
506
EXPORT_SYMBOL_NS_GPL(sdca_fdl_alloc_state, "SND_SOC_SDCA");
507
508