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
get_subaddr(struct snd_dice * dice,enum snd_dice_addr_type type,u64 offset)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
snd_dice_transaction_write(struct snd_dice * dice,enum snd_dice_addr_type type,unsigned int offset,void * buf,unsigned int len)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
snd_dice_transaction_read(struct snd_dice * dice,enum snd_dice_addr_type type,unsigned int offset,void * buf,unsigned int len)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
get_clock_info(struct snd_dice * dice,__be32 * info)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
snd_dice_transaction_get_clock_source(struct snd_dice * dice,unsigned int * source)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
snd_dice_transaction_get_rate(struct snd_dice * dice,unsigned int * rate)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
snd_dice_transaction_set_enable(struct snd_dice * dice)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
snd_dice_transaction_clear_enable(struct snd_dice * dice)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
dice_notification(struct fw_card * card,struct fw_request * request,int tcode,int destination,int source,int generation,unsigned long long offset,void * data,size_t length,void * callback_data)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
register_notification_address(struct snd_dice * dice,bool retry)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
unregister_notification_address(struct snd_dice * dice)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
snd_dice_transaction_destroy(struct snd_dice * dice)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
snd_dice_transaction_reinit(struct snd_dice * dice)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
get_subaddrs(struct snd_dice * dice)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
snd_dice_transaction_init(struct snd_dice * dice)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 }
371