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