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