Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/soc/sdca/sdca_fdl.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/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: %x-%x-%x\n",
260
tmp->vendor_id, tmp->file_id, tmp->file_version);
261
swf = tmp;
262
}
263
}
264
265
if (!swf) {
266
dev_err(dev, "failed to locate SWF\n");
267
return -ENOENT;
268
}
269
270
ret = sdca_ump_write_message(dev, interrupt->device_regmap,
271
interrupt->function_regmap,
272
interrupt->function, interrupt->entity,
273
SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset,
274
SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data,
275
swf->file_length - offsetof(struct acpi_sw_file, data));
276
release_firmware(firmware);
277
return ret;
278
}
279
280
static struct sdca_fdl_set *fdl_get_set(struct sdca_interrupt *interrupt)
281
{
282
struct device *dev = interrupt->dev;
283
struct sdca_fdl_data *fdl_data = &interrupt->function->fdl_data;
284
struct sdca_entity *xu = interrupt->entity;
285
struct sdca_control_range *range;
286
unsigned int val;
287
int i, ret;
288
289
ret = regmap_read(interrupt->function_regmap,
290
SDW_SDCA_CTL(interrupt->function->desc->adr, xu->id,
291
SDCA_CTL_XU_FDL_SET_INDEX, 0),
292
&val);
293
if (ret < 0) {
294
dev_err(dev, "failed to read FDL set index: %d\n", ret);
295
return NULL;
296
}
297
298
range = sdca_selector_find_range(dev, xu, SDCA_CTL_XU_FDL_SET_INDEX,
299
SDCA_FDL_SET_INDEX_NCOLS, 0);
300
301
val = sdca_range_search(range, SDCA_FDL_SET_INDEX_SET_NUMBER,
302
val, SDCA_FDL_SET_INDEX_FILE_SET_ID);
303
304
for (i = 0; i < fdl_data->num_sets; i++) {
305
if (fdl_data->sets[i].id == val)
306
return &fdl_data->sets[i];
307
}
308
309
dev_err(dev, "invalid fileset id: %d\n", val);
310
return NULL;
311
}
312
313
static void fdl_end(struct sdca_interrupt *interrupt)
314
{
315
struct fdl_state *fdl_state = interrupt->priv;
316
317
if (!fdl_state->set)
318
return;
319
320
fdl_state->set = NULL;
321
322
pm_runtime_put(interrupt->dev);
323
complete(&fdl_state->done);
324
325
dev_dbg(interrupt->dev, "completed FDL process\n");
326
}
327
328
static void sdca_fdl_timeout_work(struct work_struct *work)
329
{
330
struct fdl_state *fdl_state = container_of(work, struct fdl_state,
331
timeout.work);
332
struct sdca_interrupt *interrupt = fdl_state->interrupt;
333
struct device *dev = interrupt->dev;
334
335
dev_err(dev, "FDL transaction timed out\n");
336
337
guard(mutex)(&fdl_state->lock);
338
339
fdl_end(interrupt);
340
sdca_reset_function(dev, interrupt->function, interrupt->function_regmap);
341
}
342
343
static int fdl_status_process(struct sdca_interrupt *interrupt, unsigned int status)
344
{
345
struct fdl_state *fdl_state = interrupt->priv;
346
int ret;
347
348
switch (status) {
349
case SDCA_CTL_XU_FDLD_NEEDS_SET:
350
dev_dbg(interrupt->dev, "starting FDL process...\n");
351
352
pm_runtime_get(interrupt->dev);
353
complete(&fdl_state->begin);
354
355
fdl_state->file_index = 0;
356
fdl_state->set = fdl_get_set(interrupt);
357
fallthrough;
358
case SDCA_CTL_XU_FDLD_MORE_FILES_OK:
359
ret = fdl_load_file(interrupt, fdl_state->set, fdl_state->file_index);
360
if (ret) {
361
fdl_end(interrupt);
362
return SDCA_CTL_XU_FDLH_REQ_ABORT;
363
}
364
365
return SDCA_CTL_XU_FDLH_FILE_AVAILABLE;
366
case SDCA_CTL_XU_FDLD_FILE_OK:
367
if (!fdl_state->set) {
368
fdl_end(interrupt);
369
return SDCA_CTL_XU_FDLH_REQ_ABORT;
370
}
371
372
fdl_state->file_index++;
373
374
if (fdl_state->file_index < fdl_state->set->num_files)
375
return SDCA_CTL_XU_FDLH_MORE_FILES;
376
fallthrough;
377
case SDCA_CTL_XU_FDLD_COMPLETE:
378
fdl_end(interrupt);
379
return SDCA_CTL_XU_FDLH_COMPLETE;
380
default:
381
fdl_end(interrupt);
382
383
if (status & SDCA_CTL_XU_FDLD_REQ_RESET)
384
return SDCA_CTL_XU_FDLH_RESET_ACK;
385
else if (status & SDCA_CTL_XU_FDLD_REQ_ABORT)
386
return SDCA_CTL_XU_FDLH_COMPLETE;
387
388
dev_err(interrupt->dev, "invalid FDL status: %x\n", status);
389
return -EINVAL;
390
}
391
}
392
393
/**
394
* sdca_fdl_process - Process the FDL state machine
395
* @interrupt: SDCA interrupt structure
396
*
397
* Based on section 13.2.5 Flow Diagram for File Download, Host side.
398
*
399
* Return: Zero on success or a negative error code.
400
*/
401
int sdca_fdl_process(struct sdca_interrupt *interrupt)
402
{
403
struct device *dev = interrupt->dev;
404
struct sdca_entity_xu *xu = &interrupt->entity->xu;
405
struct fdl_state *fdl_state = interrupt->priv;
406
unsigned int reg, status;
407
int response, ret;
408
409
ret = sdca_ump_get_owner_host(dev, interrupt->function_regmap,
410
interrupt->function, interrupt->entity,
411
interrupt->control);
412
if (ret)
413
goto reset_function;
414
415
sdca_ump_cancel_timeout(&fdl_state->timeout);
416
417
scoped_guard(mutex, &fdl_state->lock) {
418
reg = SDW_SDCA_CTL(interrupt->function->desc->adr,
419
interrupt->entity->id, SDCA_CTL_XU_FDL_STATUS, 0);
420
ret = regmap_read(interrupt->function_regmap, reg, &status);
421
if (ret < 0) {
422
dev_err(dev, "failed to read FDL status: %d\n", ret);
423
return ret;
424
}
425
426
dev_dbg(dev, "FDL status: %#x\n", status);
427
428
ret = fdl_status_process(interrupt, status);
429
if (ret < 0)
430
goto reset_function;
431
432
response = ret;
433
434
dev_dbg(dev, "FDL response: %#x\n", response);
435
436
ret = regmap_write(interrupt->function_regmap, reg,
437
response | (status & ~SDCA_CTL_XU_FDLH_MASK));
438
if (ret < 0) {
439
dev_err(dev, "failed to set FDL status signal: %d\n", ret);
440
return ret;
441
}
442
443
ret = sdca_ump_set_owner_device(dev, interrupt->function_regmap,
444
interrupt->function,
445
interrupt->entity,
446
interrupt->control);
447
if (ret)
448
return ret;
449
450
switch (response) {
451
case SDCA_CTL_XU_FDLH_RESET_ACK:
452
dev_dbg(dev, "FDL request reset\n");
453
454
switch (xu->reset_mechanism) {
455
default:
456
dev_warn(dev, "Requested reset mechanism not implemented\n");
457
fallthrough;
458
case SDCA_XU_RESET_FUNCTION:
459
goto reset_function;
460
}
461
case SDCA_CTL_XU_FDLH_COMPLETE:
462
if (status & SDCA_CTL_XU_FDLD_REQ_ABORT ||
463
status == SDCA_CTL_XU_FDLD_COMPLETE)
464
return 0;
465
fallthrough;
466
default:
467
sdca_ump_schedule_timeout(&fdl_state->timeout, xu->max_delay);
468
return 0;
469
}
470
}
471
472
reset_function:
473
sdca_reset_function(dev, interrupt->function, interrupt->function_regmap);
474
475
return ret;
476
}
477
EXPORT_SYMBOL_NS_GPL(sdca_fdl_process, "SND_SOC_SDCA");
478
479
/**
480
* sdca_fdl_alloc_state - allocate state for an FDL interrupt
481
* @interrupt: SDCA interrupt structure.
482
*
483
* Return: Zero on success or a negative error code.
484
*/
485
int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt)
486
{
487
struct device *dev = interrupt->dev;
488
struct fdl_state *fdl_state;
489
490
fdl_state = devm_kzalloc(dev, sizeof(struct fdl_state), GFP_KERNEL);
491
if (!fdl_state)
492
return -ENOMEM;
493
494
INIT_DELAYED_WORK(&fdl_state->timeout, sdca_fdl_timeout_work);
495
init_completion(&fdl_state->begin);
496
init_completion(&fdl_state->done);
497
mutex_init(&fdl_state->lock);
498
fdl_state->interrupt = interrupt;
499
500
interrupt->priv = fdl_state;
501
502
return 0;
503
}
504
EXPORT_SYMBOL_NS_GPL(sdca_fdl_alloc_state, "SND_SOC_SDCA");
505
506