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