Merge branch 'sched-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[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{
68 unsigned int retries = 3;
69 unsigned int i;
70 __be32 info;
71 u32 mask;
72 u32 clock;
73 int err;
74retry:
75 err = get_clock_info(dice, &info);
76 if (err < 0)
77 goto end;
78
79 clock = be32_to_cpu(info);
80 if (source != UINT_MAX) {
81 mask = CLOCK_SOURCE_MASK;
82 clock &= ~mask;
83 clock |= source;
84 }
85 if (rate != UINT_MAX) {
86 for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
87 if (snd_dice_rates[i] == rate)
88 break;
89 }
90 if (i == ARRAY_SIZE(snd_dice_rates)) {
91 err = -EINVAL;
92 goto end;
93 }
94
95 mask = CLOCK_RATE_MASK;
96 clock &= ~mask;
97 clock |= i << CLOCK_RATE_SHIFT;
98 }
99 info = cpu_to_be32(clock);
100
101 if (completion_done(&dice->clock_accepted))
102 reinit_completion(&dice->clock_accepted);
103
104 err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
105 &info, 4);
106 if (err < 0)
107 goto end;
108
109 /* Timeout means it's invalid request, probably bus reset occurred. */
110 if (wait_for_completion_timeout(&dice->clock_accepted,
111 msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
112 if (retries-- == 0) {
113 err = -ETIMEDOUT;
114 goto end;
115 }
116
117 err = snd_dice_transaction_reinit(dice);
118 if (err < 0)
119 goto end;
120
121 msleep(500); /* arbitrary */
122 goto retry;
123 }
124end:
125 return err;
126}
127
128int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
129 unsigned int *source)
130{
131 __be32 info;
132 int err;
133
134 err = get_clock_info(dice, &info);
135 if (err >= 0)
136 *source = be32_to_cpu(info) & CLOCK_SOURCE_MASK;
137
138 return err;
139}
7c2d4c0c
TS
140
141int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
142{
143 __be32 info;
144 unsigned int index;
145 int err;
146
147 err = get_clock_info(dice, &info);
148 if (err < 0)
149 goto end;
150
151 index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
152 if (index >= SND_DICE_RATES_COUNT) {
153 err = -ENOSYS;
154 goto end;
155 }
156
157 *rate = snd_dice_rates[index];
158end:
159 return err;
160}
161int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
162{
163 return set_clock_info(dice, rate, UINT_MAX);
164}
165
166int snd_dice_transaction_set_enable(struct snd_dice *dice)
167{
168 __be32 value;
169 int err = 0;
170
171 if (dice->global_enabled)
172 goto end;
173
174 value = cpu_to_be32(1);
175 err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
176 get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
177 GLOBAL_ENABLE),
178 &value, 4,
179 FW_FIXED_GENERATION | dice->owner_generation);
180 if (err < 0)
181 goto end;
182
183 dice->global_enabled = true;
184end:
185 return err;
186}
187
188void snd_dice_transaction_clear_enable(struct snd_dice *dice)
189{
190 __be32 value;
191
192 value = 0;
193 snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
194 get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
195 GLOBAL_ENABLE),
196 &value, 4, FW_QUIET |
197 FW_FIXED_GENERATION | dice->owner_generation);
198
199 dice->global_enabled = false;
200}
201
202static void dice_notification(struct fw_card *card, struct fw_request *request,
203 int tcode, int destination, int source,
204 int generation, unsigned long long offset,
205 void *data, size_t length, void *callback_data)
206{
207 struct snd_dice *dice = callback_data;
208 u32 bits;
209 unsigned long flags;
210
211 if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
212 fw_send_response(card, request, RCODE_TYPE_ERROR);
213 return;
214 }
215 if ((offset & 3) != 0) {
216 fw_send_response(card, request, RCODE_ADDRESS_ERROR);
217 return;
218 }
219
220 bits = be32_to_cpup(data);
221
222 spin_lock_irqsave(&dice->lock, flags);
223 dice->notification_bits |= bits;
224 spin_unlock_irqrestore(&dice->lock, flags);
225
226 fw_send_response(card, request, RCODE_COMPLETE);
227
228 if (bits & NOTIFY_CLOCK_ACCEPTED)
229 complete(&dice->clock_accepted);
230 wake_up(&dice->hwdep_wait);
231}
232
233static int register_notification_address(struct snd_dice *dice, bool retry)
234{
235 struct fw_device *device = fw_parent_device(dice->unit);
236 __be64 *buffer;
237 unsigned int retries;
238 int err;
239
240 retries = (retry) ? 3 : 0;
241
242 buffer = kmalloc(2 * 8, GFP_KERNEL);
243 if (!buffer)
244 return -ENOMEM;
245
246 for (;;) {
247 buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
248 buffer[1] = cpu_to_be64(
249 ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
250 dice->notification_handler.offset);
251
252 dice->owner_generation = device->generation;
253 smp_rmb(); /* node_id vs. generation */
254 err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
255 get_subaddr(dice,
256 SND_DICE_ADDR_TYPE_GLOBAL,
257 GLOBAL_OWNER),
258 buffer, 2 * 8,
259 FW_FIXED_GENERATION |
260 dice->owner_generation);
261 if (err == 0) {
262 /* success */
263 if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))
264 break;
265 /* The address seems to be already registered. */
266 if (buffer[0] == buffer[1])
267 break;
268
269 dev_err(&dice->unit->device,
270 "device is already in use\n");
271 err = -EBUSY;
272 }
273 if (err != -EAGAIN || retries-- > 0)
274 break;
275
276 msleep(20);
277 }
278
279 kfree(buffer);
280
281 if (err < 0)
282 dice->owner_generation = -1;
283
284 return err;
285}
286
287static void unregister_notification_address(struct snd_dice *dice)
288{
289 struct fw_device *device = fw_parent_device(dice->unit);
290 __be64 *buffer;
291
292 buffer = kmalloc(2 * 8, GFP_KERNEL);
293 if (buffer == NULL)
294 return;
295
296 buffer[0] = cpu_to_be64(
297 ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
298 dice->notification_handler.offset);
299 buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
300 snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
301 get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
302 GLOBAL_OWNER),
303 buffer, 2 * 8, FW_QUIET |
304 FW_FIXED_GENERATION | dice->owner_generation);
305
306 kfree(buffer);
307
308 dice->owner_generation = -1;
309}
310
311void snd_dice_transaction_destroy(struct snd_dice *dice)
312{
313 struct fw_address_handler *handler = &dice->notification_handler;
314
315 if (handler->callback_data == NULL)
316 return;
317
318 unregister_notification_address(dice);
319
320 fw_core_remove_address_handler(handler);
321 handler->callback_data = NULL;
322}
323
324int snd_dice_transaction_reinit(struct snd_dice *dice)
325{
326 struct fw_address_handler *handler = &dice->notification_handler;
327
328 if (handler->callback_data == NULL)
329 return -EINVAL;
330
331 return register_notification_address(dice, false);
332}
333
334int snd_dice_transaction_init(struct snd_dice *dice)
335{
336 struct fw_address_handler *handler = &dice->notification_handler;
337 __be32 *pointers;
338 int err;
339
340 /* Use the same way which dice_interface_check() does. */
341 pointers = kmalloc(sizeof(__be32) * 10, GFP_KERNEL);
342 if (pointers == NULL)
343 return -ENOMEM;
344
345 /* Get offsets for sub-addresses */
346 err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
347 DICE_PRIVATE_SPACE,
348 pointers, sizeof(__be32) * 10, 0);
349 if (err < 0)
350 goto end;
351
352 /* Allocation callback in address space over host controller */
353 handler->length = 4;
354 handler->address_callback = dice_notification;
355 handler->callback_data = dice;
356 err = fw_core_add_address_handler(handler, &fw_high_memory_region);
357 if (err < 0) {
358 handler->callback_data = NULL;
359 goto end;
360 }
361
362 /* Register the address space */
363 err = register_notification_address(dice, true);
364 if (err < 0) {
365 fw_core_remove_address_handler(handler);
366 handler->callback_data = NULL;
367 goto end;
368 }
369
370 dice->global_offset = be32_to_cpu(pointers[0]) * 4;
371 dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
372 dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
373 dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
374 dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
375
376 /* Set up later. */
377 if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4)
378 dice->clock_caps = 1;
379end:
380 kfree(pointers);
381 return err;
382}
This page took 0.075665 seconds and 5 git commands to generate.