2 * dice_transaction.c - a part of driver for Dice based devices
4 * Copyright (c) Clemens Ladisch
5 * Copyright (c) 2014 Takashi Sakamoto
7 * Licensed under the terms of the GNU General Public License, version 2.
12 #define NOTIFICATION_TIMEOUT_MS 100
14 static u64
get_subaddr(struct snd_dice
*dice
, enum snd_dice_addr_type type
,
18 case SND_DICE_ADDR_TYPE_TX
:
19 offset
+= dice
->tx_offset
;
21 case SND_DICE_ADDR_TYPE_RX
:
22 offset
+= dice
->rx_offset
;
24 case SND_DICE_ADDR_TYPE_SYNC
:
25 offset
+= dice
->sync_offset
;
27 case SND_DICE_ADDR_TYPE_RSRV
:
28 offset
+= dice
->rsrv_offset
;
30 case SND_DICE_ADDR_TYPE_GLOBAL
:
32 offset
+= dice
->global_offset
;
35 offset
+= DICE_PRIVATE_SPACE
;
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
)
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);
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
)
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);
59 static unsigned int get_clock_info(struct snd_dice
*dice
, __be32
*info
)
61 return snd_dice_transaction_read_global(dice
, GLOBAL_CLOCK_SELECT
,
65 static int set_clock_info(struct snd_dice
*dice
,
66 unsigned int rate
, unsigned int source
)
74 err
= get_clock_info(dice
, &info
);
78 clock
= be32_to_cpu(info
);
79 if (source
!= UINT_MAX
) {
80 mask
= CLOCK_SOURCE_MASK
;
84 if (rate
!= UINT_MAX
) {
85 for (i
= 0; i
< ARRAY_SIZE(snd_dice_rates
); i
++) {
86 if (snd_dice_rates
[i
] == rate
)
89 if (i
== ARRAY_SIZE(snd_dice_rates
))
92 mask
= CLOCK_RATE_MASK
;
94 clock
|= i
<< CLOCK_RATE_SHIFT
;
96 info
= cpu_to_be32(clock
);
98 if (completion_done(&dice
->clock_accepted
))
99 reinit_completion(&dice
->clock_accepted
);
101 err
= snd_dice_transaction_write_global(dice
, GLOBAL_CLOCK_SELECT
,
106 if (wait_for_completion_timeout(&dice
->clock_accepted
,
107 msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS
)) == 0)
113 int snd_dice_transaction_get_clock_source(struct snd_dice
*dice
,
114 unsigned int *source
)
119 err
= get_clock_info(dice
, &info
);
121 *source
= be32_to_cpu(info
) & CLOCK_SOURCE_MASK
;
126 int snd_dice_transaction_get_rate(struct snd_dice
*dice
, unsigned int *rate
)
132 err
= get_clock_info(dice
, &info
);
136 index
= (be32_to_cpu(info
) & CLOCK_RATE_MASK
) >> CLOCK_RATE_SHIFT
;
137 if (index
>= SND_DICE_RATES_COUNT
) {
142 *rate
= snd_dice_rates
[index
];
146 int snd_dice_transaction_set_rate(struct snd_dice
*dice
, unsigned int rate
)
148 return set_clock_info(dice
, rate
, UINT_MAX
);
151 int snd_dice_transaction_set_enable(struct snd_dice
*dice
)
156 if (dice
->global_enabled
)
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
,
164 FW_FIXED_GENERATION
| dice
->owner_generation
);
168 dice
->global_enabled
= true;
173 void snd_dice_transaction_clear_enable(struct snd_dice
*dice
)
178 snd_fw_transaction(dice
->unit
, TCODE_WRITE_QUADLET_REQUEST
,
179 get_subaddr(dice
, SND_DICE_ADDR_TYPE_GLOBAL
,
181 &value
, 4, FW_QUIET
|
182 FW_FIXED_GENERATION
| dice
->owner_generation
);
184 dice
->global_enabled
= false;
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
)
192 struct snd_dice
*dice
= callback_data
;
196 if (tcode
!= TCODE_WRITE_QUADLET_REQUEST
) {
197 fw_send_response(card
, request
, RCODE_TYPE_ERROR
);
200 if ((offset
& 3) != 0) {
201 fw_send_response(card
, request
, RCODE_ADDRESS_ERROR
);
205 bits
= be32_to_cpup(data
);
207 spin_lock_irqsave(&dice
->lock
, flags
);
208 dice
->notification_bits
|= bits
;
209 spin_unlock_irqrestore(&dice
->lock
, flags
);
211 fw_send_response(card
, request
, RCODE_COMPLETE
);
213 if (bits
& NOTIFY_CLOCK_ACCEPTED
)
214 complete(&dice
->clock_accepted
);
215 wake_up(&dice
->hwdep_wait
);
218 static int register_notification_address(struct snd_dice
*dice
, bool retry
)
220 struct fw_device
*device
= fw_parent_device(dice
->unit
);
222 unsigned int retries
;
225 retries
= (retry
) ? 3 : 0;
227 buffer
= kmalloc(2 * 8, GFP_KERNEL
);
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
);
237 dice
->owner_generation
= device
->generation
;
238 smp_rmb(); /* node_id vs. generation */
239 err
= snd_fw_transaction(dice
->unit
, TCODE_LOCK_COMPARE_SWAP
,
241 SND_DICE_ADDR_TYPE_GLOBAL
,
244 FW_FIXED_GENERATION
|
245 dice
->owner_generation
);
248 if (buffer
[0] == cpu_to_be64(OWNER_NO_OWNER
))
250 /* The address seems to be already registered. */
251 if (buffer
[0] == buffer
[1])
254 dev_err(&dice
->unit
->device
,
255 "device is already in use\n");
258 if (err
!= -EAGAIN
|| retries
-- > 0)
267 dice
->owner_generation
= -1;
272 static void unregister_notification_address(struct snd_dice
*dice
)
274 struct fw_device
*device
= fw_parent_device(dice
->unit
);
277 buffer
= kmalloc(2 * 8, GFP_KERNEL
);
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
,
288 buffer
, 2 * 8, FW_QUIET
|
289 FW_FIXED_GENERATION
| dice
->owner_generation
);
293 dice
->owner_generation
= -1;
296 void snd_dice_transaction_destroy(struct snd_dice
*dice
)
298 struct fw_address_handler
*handler
= &dice
->notification_handler
;
300 if (handler
->callback_data
== NULL
)
303 unregister_notification_address(dice
);
305 fw_core_remove_address_handler(handler
);
306 handler
->callback_data
= NULL
;
309 int snd_dice_transaction_reinit(struct snd_dice
*dice
)
311 struct fw_address_handler
*handler
= &dice
->notification_handler
;
313 if (handler
->callback_data
== NULL
)
316 return register_notification_address(dice
, false);
319 static int get_subaddrs(struct snd_dice
*dice
)
321 static const int min_values
[10] = {
334 pointers
= kmalloc_array(ARRAY_SIZE(min_values
), sizeof(__be32
),
336 if (pointers
== NULL
)
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.
344 err
= snd_fw_transaction(dice
->unit
, TCODE_READ_BLOCK_REQUEST
,
345 DICE_PRIVATE_SPACE
, pointers
,
346 sizeof(__be32
) * ARRAY_SIZE(min_values
), 0);
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) {
359 * Check that the implemented DICE driver specification major version
362 err
= snd_fw_transaction(dice
->unit
, TCODE_READ_QUADLET_REQUEST
,
364 be32_to_cpu(pointers
[0]) * 4 + GLOBAL_VERSION
,
365 &version
, sizeof(version
), 0);
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
));
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;
383 if (be32_to_cpu(pointers
[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES
+ 4)
384 dice
->clock_caps
= 1;
390 int snd_dice_transaction_init(struct snd_dice
*dice
)
392 struct fw_address_handler
*handler
= &dice
->notification_handler
;
395 err
= get_subaddrs(dice
);
399 /* Allocation callback in address space over host controller */
401 handler
->address_callback
= dice_notification
;
402 handler
->callback_data
= dice
;
403 err
= fw_core_add_address_handler(handler
, &fw_high_memory_region
);
405 handler
->callback_data
= NULL
;
409 /* Register the address space */
410 err
= register_notification_address(dice
, true);
412 fw_core_remove_address_handler(handler
);
413 handler
->callback_data
= NULL
;