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 #define NOTIFICATION_TIMEOUT_MS	100
13 
14 static 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;
34 	}
35 	offset += DICE_PRIVATE_SPACE;
36 	return offset;
37 }
38 
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)
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 
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)
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 
59 static 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 
65 static int set_clock_info(struct snd_dice *dice,
66 			  unsigned int rate, unsigned int source)
67 {
68 	unsigned int retries = 3;
69 	unsigned int i;
70 	__be32 info;
71 	u32 mask;
72 	u32 clock;
73 	int err;
74 retry:
75 	err = get_clock_info(dice, &info);
76 	if (err < 0)
77 		goto end;
78 
79 	clock = be32_to_cpu(info);
80 	if (source != UINT_MAX) {
81 		mask = CLOCK_SOURCE_MASK;
82 		clock &= ~mask;
83 		clock |= source;
84 	}
85 	if (rate != UINT_MAX) {
86 		for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
87 			if (snd_dice_rates[i] == rate)
88 				break;
89 		}
90 		if (i == ARRAY_SIZE(snd_dice_rates)) {
91 			err = -EINVAL;
92 			goto end;
93 		}
94 
95 		mask = CLOCK_RATE_MASK;
96 		clock &= ~mask;
97 		clock |= i << CLOCK_RATE_SHIFT;
98 	}
99 	info = cpu_to_be32(clock);
100 
101 	if (completion_done(&dice->clock_accepted))
102 		reinit_completion(&dice->clock_accepted);
103 
104 	err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
105 						&info, 4);
106 	if (err < 0)
107 		goto end;
108 
109 	/* Timeout means it's invalid request, probably bus reset occurred. */
110 	if (wait_for_completion_timeout(&dice->clock_accepted,
111 			msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
112 		if (retries-- == 0) {
113 			err = -ETIMEDOUT;
114 			goto end;
115 		}
116 
117 		err = snd_dice_transaction_reinit(dice);
118 		if (err < 0)
119 			goto end;
120 
121 		msleep(500);	/* arbitrary */
122 		goto retry;
123 	}
124 end:
125 	return err;
126 }
127 
128 int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
129 					  unsigned int *source)
130 {
131 	__be32 info;
132 	int err;
133 
134 	err = get_clock_info(dice, &info);
135 	if (err >= 0)
136 		*source = be32_to_cpu(info) & CLOCK_SOURCE_MASK;
137 
138 	return err;
139 }
140 
141 int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
142 {
143 	__be32 info;
144 	unsigned int index;
145 	int err;
146 
147 	err = get_clock_info(dice, &info);
148 	if (err < 0)
149 		goto end;
150 
151 	index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
152 	if (index >= SND_DICE_RATES_COUNT) {
153 		err = -ENOSYS;
154 		goto end;
155 	}
156 
157 	*rate = snd_dice_rates[index];
158 end:
159 	return err;
160 }
161 int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
162 {
163 	return set_clock_info(dice, rate, UINT_MAX);
164 }
165 
166 int snd_dice_transaction_set_enable(struct snd_dice *dice)
167 {
168 	__be32 value;
169 	int err = 0;
170 
171 	if (dice->global_enabled)
172 		goto end;
173 
174 	value = cpu_to_be32(1);
175 	err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
176 				 get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
177 					     GLOBAL_ENABLE),
178 				 &value, 4,
179 				 FW_FIXED_GENERATION | dice->owner_generation);
180 	if (err < 0)
181 		goto end;
182 
183 	dice->global_enabled = true;
184 end:
185 	return err;
186 }
187 
188 void snd_dice_transaction_clear_enable(struct snd_dice *dice)
189 {
190 	__be32 value;
191 
192 	value = 0;
193 	snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
194 			   get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
195 				       GLOBAL_ENABLE),
196 			   &value, 4, FW_QUIET |
197 			   FW_FIXED_GENERATION | dice->owner_generation);
198 
199 	dice->global_enabled = false;
200 }
201 
202 static void dice_notification(struct fw_card *card, struct fw_request *request,
203 			      int tcode, int destination, int source,
204 			      int generation, unsigned long long offset,
205 			      void *data, size_t length, void *callback_data)
206 {
207 	struct snd_dice *dice = callback_data;
208 	u32 bits;
209 	unsigned long flags;
210 
211 	if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
212 		fw_send_response(card, request, RCODE_TYPE_ERROR);
213 		return;
214 	}
215 	if ((offset & 3) != 0) {
216 		fw_send_response(card, request, RCODE_ADDRESS_ERROR);
217 		return;
218 	}
219 
220 	bits = be32_to_cpup(data);
221 
222 	spin_lock_irqsave(&dice->lock, flags);
223 	dice->notification_bits |= bits;
224 	spin_unlock_irqrestore(&dice->lock, flags);
225 
226 	fw_send_response(card, request, RCODE_COMPLETE);
227 
228 	if (bits & NOTIFY_CLOCK_ACCEPTED)
229 		complete(&dice->clock_accepted);
230 	wake_up(&dice->hwdep_wait);
231 }
232 
233 static int register_notification_address(struct snd_dice *dice, bool retry)
234 {
235 	struct fw_device *device = fw_parent_device(dice->unit);
236 	__be64 *buffer;
237 	unsigned int retries;
238 	int err;
239 
240 	retries = (retry) ? 3 : 0;
241 
242 	buffer = kmalloc(2 * 8, GFP_KERNEL);
243 	if (!buffer)
244 		return -ENOMEM;
245 
246 	for (;;) {
247 		buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
248 		buffer[1] = cpu_to_be64(
249 			((u64)device->card->node_id << OWNER_NODE_SHIFT) |
250 			dice->notification_handler.offset);
251 
252 		dice->owner_generation = device->generation;
253 		smp_rmb(); /* node_id vs. generation */
254 		err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
255 					 get_subaddr(dice,
256 						     SND_DICE_ADDR_TYPE_GLOBAL,
257 						     GLOBAL_OWNER),
258 					 buffer, 2 * 8,
259 					 FW_FIXED_GENERATION |
260 							dice->owner_generation);
261 		if (err == 0) {
262 			/* success */
263 			if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))
264 				break;
265 			/* The address seems to be already registered. */
266 			if (buffer[0] == buffer[1])
267 				break;
268 
269 			dev_err(&dice->unit->device,
270 				"device is already in use\n");
271 			err = -EBUSY;
272 		}
273 		if (err != -EAGAIN || retries-- > 0)
274 			break;
275 
276 		msleep(20);
277 	}
278 
279 	kfree(buffer);
280 
281 	if (err < 0)
282 		dice->owner_generation = -1;
283 
284 	return err;
285 }
286 
287 static void unregister_notification_address(struct snd_dice *dice)
288 {
289 	struct fw_device *device = fw_parent_device(dice->unit);
290 	__be64 *buffer;
291 
292 	buffer = kmalloc(2 * 8, GFP_KERNEL);
293 	if (buffer == NULL)
294 		return;
295 
296 	buffer[0] = cpu_to_be64(
297 		((u64)device->card->node_id << OWNER_NODE_SHIFT) |
298 		dice->notification_handler.offset);
299 	buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
300 	snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
301 			   get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
302 				       GLOBAL_OWNER),
303 			   buffer, 2 * 8, FW_QUIET |
304 			   FW_FIXED_GENERATION | dice->owner_generation);
305 
306 	kfree(buffer);
307 
308 	dice->owner_generation = -1;
309 }
310 
311 void snd_dice_transaction_destroy(struct snd_dice *dice)
312 {
313 	struct fw_address_handler *handler = &dice->notification_handler;
314 
315 	if (handler->callback_data == NULL)
316 		return;
317 
318 	unregister_notification_address(dice);
319 
320 	fw_core_remove_address_handler(handler);
321 	handler->callback_data = NULL;
322 }
323 
324 int snd_dice_transaction_reinit(struct snd_dice *dice)
325 {
326 	struct fw_address_handler *handler = &dice->notification_handler;
327 
328 	if (handler->callback_data == NULL)
329 		return -EINVAL;
330 
331 	return register_notification_address(dice, false);
332 }
333 
334 int snd_dice_transaction_init(struct snd_dice *dice)
335 {
336 	struct fw_address_handler *handler = &dice->notification_handler;
337 	__be32 *pointers;
338 	int err;
339 
340 	/* Use the same way which dice_interface_check() does. */
341 	pointers = kmalloc(sizeof(__be32) * 10, GFP_KERNEL);
342 	if (pointers == NULL)
343 		return -ENOMEM;
344 
345 	/* Get offsets for sub-addresses */
346 	err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
347 				 DICE_PRIVATE_SPACE,
348 				 pointers, sizeof(__be32) * 10, 0);
349 	if (err < 0)
350 		goto end;
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 		goto end;
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 		goto end;
368 	}
369 
370 	dice->global_offset = be32_to_cpu(pointers[0]) * 4;
371 	dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
372 	dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
373 	dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
374 	dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
375 
376 	/* Set up later. */
377 	if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4)
378 		dice->clock_caps = 1;
379 end:
380 	kfree(pointers);
381 	return err;
382 }
383