Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/dice/dice-transaction.c
29266 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* dice_transaction.c - a part of driver for Dice based devices
4
*
5
* Copyright (c) Clemens Ladisch
6
* Copyright (c) 2014 Takashi Sakamoto
7
*/
8
9
#include "dice.h"
10
11
static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
12
u64 offset)
13
{
14
switch (type) {
15
case SND_DICE_ADDR_TYPE_TX:
16
offset += dice->tx_offset;
17
break;
18
case SND_DICE_ADDR_TYPE_RX:
19
offset += dice->rx_offset;
20
break;
21
case SND_DICE_ADDR_TYPE_SYNC:
22
offset += dice->sync_offset;
23
break;
24
case SND_DICE_ADDR_TYPE_RSRV:
25
offset += dice->rsrv_offset;
26
break;
27
case SND_DICE_ADDR_TYPE_GLOBAL:
28
default:
29
offset += dice->global_offset;
30
break;
31
}
32
offset += DICE_PRIVATE_SPACE;
33
return offset;
34
}
35
36
int snd_dice_transaction_write(struct snd_dice *dice,
37
enum snd_dice_addr_type type,
38
unsigned int offset, void *buf, unsigned int len)
39
{
40
return snd_fw_transaction(dice->unit,
41
(len == 4) ? TCODE_WRITE_QUADLET_REQUEST :
42
TCODE_WRITE_BLOCK_REQUEST,
43
get_subaddr(dice, type, offset), buf, len, 0);
44
}
45
46
int snd_dice_transaction_read(struct snd_dice *dice,
47
enum snd_dice_addr_type type, unsigned int offset,
48
void *buf, unsigned int len)
49
{
50
return snd_fw_transaction(dice->unit,
51
(len == 4) ? TCODE_READ_QUADLET_REQUEST :
52
TCODE_READ_BLOCK_REQUEST,
53
get_subaddr(dice, type, offset), buf, len, 0);
54
}
55
56
static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
57
{
58
return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
59
info, 4);
60
}
61
62
int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
63
unsigned int *source)
64
{
65
__be32 info;
66
int err;
67
68
err = get_clock_info(dice, &info);
69
if (err >= 0)
70
*source = be32_to_cpu(info) & CLOCK_SOURCE_MASK;
71
72
return err;
73
}
74
75
int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
76
{
77
__be32 info;
78
unsigned int index;
79
int err;
80
81
err = get_clock_info(dice, &info);
82
if (err < 0)
83
goto end;
84
85
index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
86
if (index >= SND_DICE_RATES_COUNT) {
87
err = -ENOSYS;
88
goto end;
89
}
90
91
*rate = snd_dice_rates[index];
92
end:
93
return err;
94
}
95
96
int snd_dice_transaction_set_enable(struct snd_dice *dice)
97
{
98
__be32 value;
99
int err = 0;
100
101
if (dice->global_enabled)
102
goto end;
103
104
value = cpu_to_be32(1);
105
err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
106
get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
107
GLOBAL_ENABLE),
108
&value, 4,
109
FW_FIXED_GENERATION | dice->owner_generation);
110
if (err < 0)
111
goto end;
112
113
dice->global_enabled = true;
114
end:
115
return err;
116
}
117
118
void snd_dice_transaction_clear_enable(struct snd_dice *dice)
119
{
120
__be32 value;
121
122
value = 0;
123
snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
124
get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
125
GLOBAL_ENABLE),
126
&value, 4, FW_QUIET |
127
FW_FIXED_GENERATION | dice->owner_generation);
128
129
dice->global_enabled = false;
130
}
131
132
static void dice_notification(struct fw_card *card, struct fw_request *request,
133
int tcode, int destination, int source,
134
int generation, unsigned long long offset,
135
void *data, size_t length, void *callback_data)
136
{
137
struct snd_dice *dice = callback_data;
138
u32 bits;
139
140
if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
141
fw_send_response(card, request, RCODE_TYPE_ERROR);
142
return;
143
}
144
if ((offset & 3) != 0) {
145
fw_send_response(card, request, RCODE_ADDRESS_ERROR);
146
return;
147
}
148
149
bits = be32_to_cpup(data);
150
151
scoped_guard(spinlock_irqsave, &dice->lock) {
152
dice->notification_bits |= bits;
153
}
154
155
fw_send_response(card, request, RCODE_COMPLETE);
156
157
if (bits & NOTIFY_CLOCK_ACCEPTED)
158
complete(&dice->clock_accepted);
159
wake_up(&dice->hwdep_wait);
160
}
161
162
static int register_notification_address(struct snd_dice *dice, bool retry)
163
{
164
struct fw_device *device = fw_parent_device(dice->unit);
165
__be64 *buffer;
166
unsigned int retries;
167
int err;
168
169
retries = (retry) ? 3 : 0;
170
171
buffer = kmalloc(2 * 8, GFP_KERNEL);
172
if (!buffer)
173
return -ENOMEM;
174
175
for (;;) {
176
buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
177
buffer[1] = cpu_to_be64(
178
((u64)device->card->node_id << OWNER_NODE_SHIFT) |
179
dice->notification_handler.offset);
180
181
dice->owner_generation = device->generation;
182
smp_rmb(); /* node_id vs. generation */
183
err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
184
get_subaddr(dice,
185
SND_DICE_ADDR_TYPE_GLOBAL,
186
GLOBAL_OWNER),
187
buffer, 2 * 8,
188
FW_FIXED_GENERATION |
189
dice->owner_generation);
190
if (err == 0) {
191
/* success */
192
if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))
193
break;
194
/* The address seems to be already registered. */
195
if (buffer[0] == buffer[1])
196
break;
197
198
dev_err(&dice->unit->device,
199
"device is already in use\n");
200
err = -EBUSY;
201
}
202
if (err != -EAGAIN || retries-- > 0)
203
break;
204
205
msleep(20);
206
}
207
208
kfree(buffer);
209
210
if (err < 0)
211
dice->owner_generation = -1;
212
213
return err;
214
}
215
216
static void unregister_notification_address(struct snd_dice *dice)
217
{
218
struct fw_device *device = fw_parent_device(dice->unit);
219
__be64 *buffer;
220
221
buffer = kmalloc(2 * 8, GFP_KERNEL);
222
if (buffer == NULL)
223
return;
224
225
buffer[0] = cpu_to_be64(
226
((u64)device->card->node_id << OWNER_NODE_SHIFT) |
227
dice->notification_handler.offset);
228
buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
229
snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
230
get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
231
GLOBAL_OWNER),
232
buffer, 2 * 8, FW_QUIET |
233
FW_FIXED_GENERATION | dice->owner_generation);
234
235
kfree(buffer);
236
237
dice->owner_generation = -1;
238
}
239
240
void snd_dice_transaction_destroy(struct snd_dice *dice)
241
{
242
struct fw_address_handler *handler = &dice->notification_handler;
243
244
if (handler->callback_data == NULL)
245
return;
246
247
unregister_notification_address(dice);
248
249
fw_core_remove_address_handler(handler);
250
handler->callback_data = NULL;
251
}
252
253
int snd_dice_transaction_reinit(struct snd_dice *dice)
254
{
255
struct fw_address_handler *handler = &dice->notification_handler;
256
257
if (handler->callback_data == NULL)
258
return -EINVAL;
259
260
return register_notification_address(dice, false);
261
}
262
263
static int get_subaddrs(struct snd_dice *dice)
264
{
265
static const int min_values[10] = {
266
10, 0x60 / 4,
267
10, 0x18 / 4,
268
10, 0x18 / 4,
269
0, 0,
270
0, 0,
271
};
272
__be32 *pointers;
273
__be32 version;
274
u32 data;
275
unsigned int i;
276
int err;
277
278
pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
279
GFP_KERNEL);
280
if (pointers == NULL)
281
return -ENOMEM;
282
283
/*
284
* Check that the sub address spaces exist and are located inside the
285
* private address space. The minimum values are chosen so that all
286
* minimally required registers are included.
287
*/
288
err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
289
DICE_PRIVATE_SPACE, pointers,
290
sizeof(__be32) * ARRAY_SIZE(min_values), 0);
291
if (err < 0)
292
goto end;
293
294
for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
295
data = be32_to_cpu(pointers[i]);
296
if (data < min_values[i] || data >= 0x40000) {
297
err = -ENODEV;
298
goto end;
299
}
300
}
301
302
if (be32_to_cpu(pointers[1]) > 0x18) {
303
/*
304
* Check that the implemented DICE driver specification major
305
* version number matches.
306
*/
307
err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
308
DICE_PRIVATE_SPACE +
309
be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
310
&version, sizeof(version), 0);
311
if (err < 0)
312
goto end;
313
314
if ((version & cpu_to_be32(0xff000000)) !=
315
cpu_to_be32(0x01000000)) {
316
dev_err(&dice->unit->device,
317
"unknown DICE version: 0x%08x\n",
318
be32_to_cpu(version));
319
err = -ENODEV;
320
goto end;
321
}
322
323
/* Set up later. */
324
dice->clock_caps = 1;
325
}
326
327
dice->global_offset = be32_to_cpu(pointers[0]) * 4;
328
dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
329
dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
330
331
/* Old firmware doesn't support these fields. */
332
if (pointers[7])
333
dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
334
if (pointers[9])
335
dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
336
end:
337
kfree(pointers);
338
return err;
339
}
340
341
int snd_dice_transaction_init(struct snd_dice *dice)
342
{
343
struct fw_address_handler *handler = &dice->notification_handler;
344
int err;
345
346
err = get_subaddrs(dice);
347
if (err < 0)
348
return err;
349
350
/* Allocation callback in address space over host controller */
351
handler->length = 4;
352
handler->address_callback = dice_notification;
353
handler->callback_data = dice;
354
err = fw_core_add_address_handler(handler, &fw_high_memory_region);
355
if (err < 0) {
356
handler->callback_data = NULL;
357
return err;
358
}
359
360
/* Register the address space */
361
err = register_notification_address(dice, true);
362
if (err < 0) {
363
fw_core_remove_address_handler(handler);
364
handler->callback_data = NULL;
365
}
366
367
return err;
368
}
369
370