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 static u64
get_subaddr(struct snd_dice
*dice
, enum snd_dice_addr_type type
,
16 case SND_DICE_ADDR_TYPE_TX
:
17 offset
+= dice
->tx_offset
;
19 case SND_DICE_ADDR_TYPE_RX
:
20 offset
+= dice
->rx_offset
;
22 case SND_DICE_ADDR_TYPE_SYNC
:
23 offset
+= dice
->sync_offset
;
25 case SND_DICE_ADDR_TYPE_RSRV
:
26 offset
+= dice
->rsrv_offset
;
28 case SND_DICE_ADDR_TYPE_GLOBAL
:
30 offset
+= dice
->global_offset
;
33 offset
+= DICE_PRIVATE_SPACE
;
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
)
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);
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
)
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);
57 static unsigned int get_clock_info(struct snd_dice
*dice
, __be32
*info
)
59 return snd_dice_transaction_read_global(dice
, GLOBAL_CLOCK_SELECT
,
63 int snd_dice_transaction_get_clock_source(struct snd_dice
*dice
,
69 err
= get_clock_info(dice
, &info
);
71 *source
= be32_to_cpu(info
) & CLOCK_SOURCE_MASK
;
76 int snd_dice_transaction_get_rate(struct snd_dice
*dice
, unsigned int *rate
)
82 err
= get_clock_info(dice
, &info
);
86 index
= (be32_to_cpu(info
) & CLOCK_RATE_MASK
) >> CLOCK_RATE_SHIFT
;
87 if (index
>= SND_DICE_RATES_COUNT
) {
92 *rate
= snd_dice_rates
[index
];
97 int snd_dice_transaction_set_enable(struct snd_dice
*dice
)
102 if (dice
->global_enabled
)
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
,
110 FW_FIXED_GENERATION
| dice
->owner_generation
);
114 dice
->global_enabled
= true;
119 void snd_dice_transaction_clear_enable(struct snd_dice
*dice
)
124 snd_fw_transaction(dice
->unit
, TCODE_WRITE_QUADLET_REQUEST
,
125 get_subaddr(dice
, SND_DICE_ADDR_TYPE_GLOBAL
,
127 &value
, 4, FW_QUIET
|
128 FW_FIXED_GENERATION
| dice
->owner_generation
);
130 dice
->global_enabled
= false;
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
)
138 struct snd_dice
*dice
= callback_data
;
142 if (tcode
!= TCODE_WRITE_QUADLET_REQUEST
) {
143 fw_send_response(card
, request
, RCODE_TYPE_ERROR
);
146 if ((offset
& 3) != 0) {
147 fw_send_response(card
, request
, RCODE_ADDRESS_ERROR
);
151 bits
= be32_to_cpup(data
);
153 spin_lock_irqsave(&dice
->lock
, flags
);
154 dice
->notification_bits
|= bits
;
155 spin_unlock_irqrestore(&dice
->lock
, flags
);
157 fw_send_response(card
, request
, RCODE_COMPLETE
);
159 if (bits
& NOTIFY_LOCK_CHG
)
160 complete(&dice
->clock_accepted
);
161 wake_up(&dice
->hwdep_wait
);
164 static int register_notification_address(struct snd_dice
*dice
, bool retry
)
166 struct fw_device
*device
= fw_parent_device(dice
->unit
);
168 unsigned int retries
;
171 retries
= (retry
) ? 3 : 0;
173 buffer
= kmalloc(2 * 8, GFP_KERNEL
);
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
);
183 dice
->owner_generation
= device
->generation
;
184 smp_rmb(); /* node_id vs. generation */
185 err
= snd_fw_transaction(dice
->unit
, TCODE_LOCK_COMPARE_SWAP
,
187 SND_DICE_ADDR_TYPE_GLOBAL
,
190 FW_FIXED_GENERATION
|
191 dice
->owner_generation
);
194 if (buffer
[0] == cpu_to_be64(OWNER_NO_OWNER
))
196 /* The address seems to be already registered. */
197 if (buffer
[0] == buffer
[1])
200 dev_err(&dice
->unit
->device
,
201 "device is already in use\n");
204 if (err
!= -EAGAIN
|| retries
-- > 0)
213 dice
->owner_generation
= -1;
218 static void unregister_notification_address(struct snd_dice
*dice
)
220 struct fw_device
*device
= fw_parent_device(dice
->unit
);
223 buffer
= kmalloc(2 * 8, GFP_KERNEL
);
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
,
234 buffer
, 2 * 8, FW_QUIET
|
235 FW_FIXED_GENERATION
| dice
->owner_generation
);
239 dice
->owner_generation
= -1;
242 void snd_dice_transaction_destroy(struct snd_dice
*dice
)
244 struct fw_address_handler
*handler
= &dice
->notification_handler
;
246 if (handler
->callback_data
== NULL
)
249 unregister_notification_address(dice
);
251 fw_core_remove_address_handler(handler
);
252 handler
->callback_data
= NULL
;
255 int snd_dice_transaction_reinit(struct snd_dice
*dice
)
257 struct fw_address_handler
*handler
= &dice
->notification_handler
;
259 if (handler
->callback_data
== NULL
)
262 return register_notification_address(dice
, false);
265 static int get_subaddrs(struct snd_dice
*dice
)
267 static const int min_values
[10] = {
280 pointers
= kmalloc_array(ARRAY_SIZE(min_values
), sizeof(__be32
),
282 if (pointers
== NULL
)
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.
290 err
= snd_fw_transaction(dice
->unit
, TCODE_READ_BLOCK_REQUEST
,
291 DICE_PRIVATE_SPACE
, pointers
,
292 sizeof(__be32
) * ARRAY_SIZE(min_values
), 0);
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) {
304 if (be32_to_cpu(pointers
[1]) > 0x18) {
306 * Check that the implemented DICE driver specification major
307 * version number matches.
309 err
= snd_fw_transaction(dice
->unit
, TCODE_READ_QUADLET_REQUEST
,
311 be32_to_cpu(pointers
[0]) * 4 + GLOBAL_VERSION
,
312 &version
, sizeof(version
), 0);
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
));
326 dice
->clock_caps
= 1;
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;
333 /* Old firmware doesn't support these fields. */
335 dice
->sync_offset
= be32_to_cpu(pointers
[6]) * 4;
337 dice
->rsrv_offset
= be32_to_cpu(pointers
[8]) * 4;
343 int snd_dice_transaction_init(struct snd_dice
*dice
)
345 struct fw_address_handler
*handler
= &dice
->notification_handler
;
348 err
= get_subaddrs(dice
);
352 /* Allocation callback in address space over host controller */
354 handler
->address_callback
= dice_notification
;
355 handler
->callback_data
= dice
;
356 err
= fw_core_add_address_handler(handler
, &fw_high_memory_region
);
358 handler
->callback_data
= NULL
;
362 /* Register the address space */
363 err
= register_notification_address(dice
, true);
365 fw_core_remove_address_handler(handler
);
366 handler
->callback_data
= NULL
;