blob: a4ff4e0bc0af1ad143471624df0c00a89eca27c4 [file] [log] [blame]
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +09001/*
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
Takashi Sakamoto2eb65d62015-12-31 13:58:14 +090012#define NOTIFICATION_TIMEOUT_MS (2 * MSEC_PER_SEC)
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +090013
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;
Fengguang Wuea09dd32014-12-02 04:03:16 +080034 }
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +090035 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{
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +090068 unsigned int i;
69 __be32 info;
70 u32 mask;
71 u32 clock;
72 int err;
Takashi Sakamotoa2875a92015-12-31 13:58:13 +090073
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +090074 err = get_clock_info(dice, &info);
75 if (err < 0)
Takashi Sakamotoa2875a92015-12-31 13:58:13 +090076 return err;
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +090077
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 }
Takashi Sakamotoa2875a92015-12-31 13:58:13 +090089 if (i == ARRAY_SIZE(snd_dice_rates))
90 return -EINVAL;
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +090091
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)
Takashi Sakamotoa2875a92015-12-31 13:58:13 +0900104 return err;
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900105
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900106 if (wait_for_completion_timeout(&dice->clock_accepted,
Takashi Sakamotoa2875a92015-12-31 13:58:13 +0900107 msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0)
108 return -ETIMEDOUT;
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900109
Takashi Sakamotoa2875a92015-12-31 13:58:13 +0900110 return 0;
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900111}
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}
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900125
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
Takashi Sakamoto4a47a872015-12-31 13:58:11 +0900319static int get_subaddrs(struct snd_dice *dice)
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900320{
Takashi Sakamoto4a47a872015-12-31 13:58:11 +0900321 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 };
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900328 __be32 *pointers;
Takashi Sakamoto4a47a872015-12-31 13:58:11 +0900329 __be32 version;
330 u32 data;
331 unsigned int i;
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900332 int err;
333
Takashi Sakamoto4a47a872015-12-31 13:58:11 +0900334 pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
335 GFP_KERNEL);
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900336 if (pointers == NULL)
337 return -ENOMEM;
338
Takashi Sakamoto4a47a872015-12-31 13:58:11 +0900339 /*
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 */
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900344 err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
Takashi Sakamoto4a47a872015-12-31 13:58:11 +0900345 DICE_PRIVATE_SPACE, pointers,
346 sizeof(__be32) * ARRAY_SIZE(min_values), 0);
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900347 if (err < 0)
348 goto end;
349
Takashi Sakamoto4a47a872015-12-31 13:58:11 +0900350 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 }
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900356 }
357
Takashi Sakamoto4a47a872015-12-31 13:58:11 +0900358 /*
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;
Takashi Sakamoto7c2d4c02014-11-29 00:59:13 +0900373 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}
Takashi Sakamoto4a47a872015-12-31 13:58:11 +0900389
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}