]> git.karo-electronics.de Git - karo-tx-linux.git/blob - sound/firewire/dice/dice-transaction.c
usb: chipidea: udc: remove unused value assignment
[karo-tx-linux.git] / sound / firewire / dice / dice-transaction.c
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 (2 * MSEC_PER_SEC)
13
14 static 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;
34         }
35         offset += DICE_PRIVATE_SPACE;
36         return offset;
37 }
38
39 int 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
49 int 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
59 static 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
65 static int set_clock_info(struct snd_dice *dice,
66                           unsigned int rate, unsigned int source)
67 {
68         unsigned int i;
69         __be32 info;
70         u32 mask;
71         u32 clock;
72         int err;
73
74         err = get_clock_info(dice, &info);
75         if (err < 0)
76                 return err;
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                 }
89                 if (i == ARRAY_SIZE(snd_dice_rates))
90                         return -EINVAL;
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)
104                 return err;
105
106         if (wait_for_completion_timeout(&dice->clock_accepted,
107                         msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0)
108                 return -ETIMEDOUT;
109
110         return 0;
111 }
112
113 int 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 }
125
126 int 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];
143 end:
144         return err;
145 }
146 int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
147 {
148         return set_clock_info(dice, rate, UINT_MAX);
149 }
150
151 int 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;
169 end:
170         return err;
171 }
172
173 void 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
187 static 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
218 static 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
272 static 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
296 void 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
309 int 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
319 static int get_subaddrs(struct snd_dice *dice)
320 {
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         };
328         __be32 *pointers;
329         __be32 version;
330         u32 data;
331         unsigned int i;
332         int err;
333
334         pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
335                                  GFP_KERNEL);
336         if (pointers == NULL)
337                 return -ENOMEM;
338
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          */
344         err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
345                                  DICE_PRIVATE_SPACE, pointers,
346                                  sizeof(__be32) * ARRAY_SIZE(min_values), 0);
347         if (err < 0)
348                 goto end;
349
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                 }
356         }
357
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;
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;
385 end:
386         kfree(pointers);
387         return err;
388 }
389
390 int 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 }