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 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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
265 static int get_subaddrs(struct snd_dice *dice)
266 {
267 	static const int min_values[10] = {
268 		10, 0x64 / 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 	/*
305 	 * Check that the implemented DICE driver specification major version
306 	 * number matches.
307 	 */
308 	err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST,
309 				 DICE_PRIVATE_SPACE +
310 				 be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION,
311 				 &version, sizeof(version), 0);
312 	if (err < 0)
313 		goto end;
314 
315 	if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) {
316 		dev_err(&dice->unit->device,
317 			"unknown DICE version: 0x%08x\n", be32_to_cpu(version));
318 		err = -ENODEV;
319 		goto end;
320 	}
321 
322 	dice->global_offset = be32_to_cpu(pointers[0]) * 4;
323 	dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
324 	dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
325 	dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
326 	dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
327 
328 	/* Set up later. */
329 	if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4)
330 		dice->clock_caps = 1;
331 end:
332 	kfree(pointers);
333 	return err;
334 }
335 
336 int snd_dice_transaction_init(struct snd_dice *dice)
337 {
338 	struct fw_address_handler *handler = &dice->notification_handler;
339 	int err;
340 
341 	err = get_subaddrs(dice);
342 	if (err < 0)
343 		return err;
344 
345 	/* Allocation callback in address space over host controller */
346 	handler->length = 4;
347 	handler->address_callback = dice_notification;
348 	handler->callback_data = dice;
349 	err = fw_core_add_address_handler(handler, &fw_high_memory_region);
350 	if (err < 0) {
351 		handler->callback_data = NULL;
352 		return err;
353 	}
354 
355 	/* Register the address space */
356 	err = register_notification_address(dice, true);
357 	if (err < 0) {
358 		fw_core_remove_address_handler(handler);
359 		handler->callback_data = NULL;
360 	}
361 
362 	return err;
363 }
364