Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/firewire/dice/dice-transaction.c
26424 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
unsigned long flags;
140
141
if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
142
fw_send_response(card, request, RCODE_TYPE_ERROR);
143
return;
144
}
145
if ((offset & 3) != 0) {
146
fw_send_response(card, request, RCODE_ADDRESS_ERROR);
147
return;
148
}
149
150
bits = be32_to_cpup(data);
151
152
spin_lock_irqsave(&dice->lock, flags);
153
dice->notification_bits |= bits;
154
spin_unlock_irqrestore(&dice->lock, flags);
155
156
fw_send_response(card, request, RCODE_COMPLETE);
157
158
if (bits & NOTIFY_CLOCK_ACCEPTED)
159
complete(&dice->clock_accepted);
160
wake_up(&dice->hwdep_wait);
161
}
162
163
static int register_notification_address(struct snd_dice *dice, bool retry)
164
{
165
struct fw_device *device = fw_parent_device(dice->unit);
166
__be64 *buffer;
167
unsigned int retries;
168
int err;
169
170
retries = (retry) ? 3 : 0;
171
172
buffer = kmalloc(2 * 8, GFP_KERNEL);
173
if (!buffer)
174
return -ENOMEM;
175
176
for (;;) {
177
buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
178
buffer[1] = cpu_to_be64(
179
((u64)device->card->node_id << OWNER_NODE_SHIFT) |
180
dice->notification_handler.offset);
181
182
dice->owner_generation = device->generation;
183
smp_rmb(); /* node_id vs. generation */
184
err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
185
get_subaddr(dice,
186
SND_DICE_ADDR_TYPE_GLOBAL,
187
GLOBAL_OWNER),
188
buffer, 2 * 8,
189
FW_FIXED_GENERATION |
190
dice->owner_generation);
191
if (err == 0) {
192
/* success */
193
if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))
194
break;
195
/* The address seems to be already registered. */
196
if (buffer[0] == buffer[1])
197
break;
198
199
dev_err(&dice->unit->device,
200
"device is already in use\n");
201
err = -EBUSY;
202
}
203
if (err != -EAGAIN || retries-- > 0)
204
break;
205
206
msleep(20);
207
}
208
209
kfree(buffer);
210
211
if (err < 0)
212
dice->owner_generation = -1;
213
214
return err;
215
}
216
217
static void unregister_notification_address(struct snd_dice *dice)
218
{
219
struct fw_device *device = fw_parent_device(dice->unit);
220
__be64 *buffer;
221
222
buffer = kmalloc(2 * 8, GFP_KERNEL);
223
if (buffer == NULL)
224
return;
225
226
buffer[0] = cpu_to_be64(
227
((u64)device->card->node_id << OWNER_NODE_SHIFT) |
228
dice->notification_handler.offset);
229
buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
230
snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
231
get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
232
GLOBAL_OWNER),
233
buffer, 2 * 8, FW_QUIET |
234
FW_FIXED_GENERATION | dice->owner_generation);
235
236
kfree(buffer);
237
238
dice->owner_generation = -1;
239
}
240
241
void snd_dice_transaction_destroy(struct snd_dice *dice)
242
{
243
struct fw_address_handler *handler = &dice->notification_handler;
244
245
if (handler->callback_data == NULL)
246
return;
247
248
unregister_notification_address(dice);
249
250
fw_core_remove_address_handler(handler);
251
handler->callback_data = NULL;
252
}
253
254
int snd_dice_transaction_reinit(struct snd_dice *dice)
255
{
256
struct fw_address_handler *handler = &dice->notification_handler;
257
258
if (handler->callback_data == NULL)
259
return -EINVAL;
260
261
return register_notification_address(dice, false);
262
}
263
264
static int get_subaddrs(struct snd_dice *dice)
265
{
266
static const int min_values[10] = {
267
10, 0x60 / 4,
268
10, 0x18 / 4,
269
10, 0x18 / 4,
270
0, 0,
271
0, 0,
272
};
273
__be32 *pointers;
274
__be32 version;
275
u32 data;
276
unsigned int i;
277
int err;
278
279
pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
280
GFP_KERNEL);
281
if (pointers == NULL)
282
return -ENOMEM;
283
284
/*
285
* Check that the sub address spaces exist and are located inside the
286
* private address space. The minimum values are chosen so that all
287
* minimally required registers are included.
288
*/
289
err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
290
DICE_PRIVATE_SPACE, pointers,
291
sizeof(__be32) * ARRAY_SIZE(min_values), 0);
292
if (err < 0)
293
goto end;
294
295
for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
296
data = be32_to_cpu(pointers[i]);
297
if (data < min_values[i] || data >= 0x40000) {
298
err = -ENODEV;
299
goto end;
300
}
301
}
302
303
if (be32_to_cpu(pointers[1]) > 0x18) {
304
/*
305
* Check that the implemented DICE driver specification major
306
* version number matches.
307
*/
308
err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
309
DICE_PRIVATE_SPACE +
310
be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
311
&version, sizeof(version), 0);
312
if (err < 0)
313
goto end;
314
315
if ((version & cpu_to_be32(0xff000000)) !=
316
cpu_to_be32(0x01000000)) {
317
dev_err(&dice->unit->device,
318
"unknown DICE version: 0x%08x\n",
319
be32_to_cpu(version));
320
err = -ENODEV;
321
goto end;
322
}
323
324
/* Set up later. */
325
dice->clock_caps = 1;
326
}
327
328
dice->global_offset = be32_to_cpu(pointers[0]) * 4;
329
dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
330
dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
331
332
/* Old firmware doesn't support these fields. */
333
if (pointers[7])
334
dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
335
if (pointers[9])
336
dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
337
end:
338
kfree(pointers);
339
return err;
340
}
341
342
int snd_dice_transaction_init(struct snd_dice *dice)
343
{
344
struct fw_address_handler *handler = &dice->notification_handler;
345
int err;
346
347
err = get_subaddrs(dice);
348
if (err < 0)
349
return err;
350
351
/* Allocation callback in address space over host controller */
352
handler->length = 4;
353
handler->address_callback = dice_notification;
354
handler->callback_data = dice;
355
err = fw_core_add_address_handler(handler, &fw_high_memory_region);
356
if (err < 0) {
357
handler->callback_data = NULL;
358
return err;
359
}
360
361
/* Register the address space */
362
err = register_notification_address(dice, true);
363
if (err < 0) {
364
fw_core_remove_address_handler(handler);
365
handler->callback_data = NULL;
366
}
367
368
return err;
369
}
370
371