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
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;
34	}
35	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{
68	unsigned int retries = 3;
69	unsigned int i;
70	__be32 info;
71	u32 mask;
72	u32 clock;
73	int err;
74retry:
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	}
124end:
125	return err;
126}
127
128int 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
141int 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];
158end:
159	return err;
160}
161int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
162{
163	return set_clock_info(dice, rate, UINT_MAX);
164}
165
166int 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;
184end:
185	return err;
186}
187
188void 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
202static 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
233static 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
287static 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
311void 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
324int 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
334int 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;
379end:
380	kfree(pointers);
381	return err;
382}
383