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 (2 * MSEC_PER_SEC) 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 i; 69 __be32 info; 70 u32 mask; 71 u32 clock; 72 int err; 73 74 err = get_clock_info(dice, &info); 75 if (err < 0) 76 return err; 77 78 clock = be32_to_cpu(info); 79 if (source != UINT_MAX) { 80 mask = CLOCK_SOURCE_MASK; 81 clock &= ~mask; 82 clock |= source; 83 } 84 if (rate != UINT_MAX) { 85 for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) { 86 if (snd_dice_rates[i] == rate) 87 break; 88 } 89 if (i == ARRAY_SIZE(snd_dice_rates)) 90 return -EINVAL; 91 92 mask = CLOCK_RATE_MASK; 93 clock &= ~mask; 94 clock |= i << CLOCK_RATE_SHIFT; 95 } 96 info = cpu_to_be32(clock); 97 98 if (completion_done(&dice->clock_accepted)) 99 reinit_completion(&dice->clock_accepted); 100 101 err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT, 102 &info, 4); 103 if (err < 0) 104 return err; 105 106 if (wait_for_completion_timeout(&dice->clock_accepted, 107 msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) 108 return -ETIMEDOUT; 109 110 return 0; 111 } 112 113 int snd_dice_transaction_get_clock_source(struct snd_dice *dice, 114 unsigned int *source) 115 { 116 __be32 info; 117 int err; 118 119 err = get_clock_info(dice, &info); 120 if (err >= 0) 121 *source = be32_to_cpu(info) & CLOCK_SOURCE_MASK; 122 123 return err; 124 } 125 126 int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate) 127 { 128 __be32 info; 129 unsigned int index; 130 int err; 131 132 err = get_clock_info(dice, &info); 133 if (err < 0) 134 goto end; 135 136 index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT; 137 if (index >= SND_DICE_RATES_COUNT) { 138 err = -ENOSYS; 139 goto end; 140 } 141 142 *rate = snd_dice_rates[index]; 143 end: 144 return err; 145 } 146 int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate) 147 { 148 return set_clock_info(dice, rate, UINT_MAX); 149 } 150 151 int snd_dice_transaction_set_enable(struct snd_dice *dice) 152 { 153 __be32 value; 154 int err = 0; 155 156 if (dice->global_enabled) 157 goto end; 158 159 value = cpu_to_be32(1); 160 err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, 161 get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL, 162 GLOBAL_ENABLE), 163 &value, 4, 164 FW_FIXED_GENERATION | dice->owner_generation); 165 if (err < 0) 166 goto end; 167 168 dice->global_enabled = true; 169 end: 170 return err; 171 } 172 173 void snd_dice_transaction_clear_enable(struct snd_dice *dice) 174 { 175 __be32 value; 176 177 value = 0; 178 snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST, 179 get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL, 180 GLOBAL_ENABLE), 181 &value, 4, FW_QUIET | 182 FW_FIXED_GENERATION | dice->owner_generation); 183 184 dice->global_enabled = false; 185 } 186 187 static void dice_notification(struct fw_card *card, struct fw_request *request, 188 int tcode, int destination, int source, 189 int generation, unsigned long long offset, 190 void *data, size_t length, void *callback_data) 191 { 192 struct snd_dice *dice = callback_data; 193 u32 bits; 194 unsigned long flags; 195 196 if (tcode != TCODE_WRITE_QUADLET_REQUEST) { 197 fw_send_response(card, request, RCODE_TYPE_ERROR); 198 return; 199 } 200 if ((offset & 3) != 0) { 201 fw_send_response(card, request, RCODE_ADDRESS_ERROR); 202 return; 203 } 204 205 bits = be32_to_cpup(data); 206 207 spin_lock_irqsave(&dice->lock, flags); 208 dice->notification_bits |= bits; 209 spin_unlock_irqrestore(&dice->lock, flags); 210 211 fw_send_response(card, request, RCODE_COMPLETE); 212 213 if (bits & NOTIFY_CLOCK_ACCEPTED) 214 complete(&dice->clock_accepted); 215 wake_up(&dice->hwdep_wait); 216 } 217 218 static int register_notification_address(struct snd_dice *dice, bool retry) 219 { 220 struct fw_device *device = fw_parent_device(dice->unit); 221 __be64 *buffer; 222 unsigned int retries; 223 int err; 224 225 retries = (retry) ? 3 : 0; 226 227 buffer = kmalloc(2 * 8, GFP_KERNEL); 228 if (!buffer) 229 return -ENOMEM; 230 231 for (;;) { 232 buffer[0] = cpu_to_be64(OWNER_NO_OWNER); 233 buffer[1] = cpu_to_be64( 234 ((u64)device->card->node_id << OWNER_NODE_SHIFT) | 235 dice->notification_handler.offset); 236 237 dice->owner_generation = device->generation; 238 smp_rmb(); /* node_id vs. generation */ 239 err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, 240 get_subaddr(dice, 241 SND_DICE_ADDR_TYPE_GLOBAL, 242 GLOBAL_OWNER), 243 buffer, 2 * 8, 244 FW_FIXED_GENERATION | 245 dice->owner_generation); 246 if (err == 0) { 247 /* success */ 248 if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER)) 249 break; 250 /* The address seems to be already registered. */ 251 if (buffer[0] == buffer[1]) 252 break; 253 254 dev_err(&dice->unit->device, 255 "device is already in use\n"); 256 err = -EBUSY; 257 } 258 if (err != -EAGAIN || retries-- > 0) 259 break; 260 261 msleep(20); 262 } 263 264 kfree(buffer); 265 266 if (err < 0) 267 dice->owner_generation = -1; 268 269 return err; 270 } 271 272 static void unregister_notification_address(struct snd_dice *dice) 273 { 274 struct fw_device *device = fw_parent_device(dice->unit); 275 __be64 *buffer; 276 277 buffer = kmalloc(2 * 8, GFP_KERNEL); 278 if (buffer == NULL) 279 return; 280 281 buffer[0] = cpu_to_be64( 282 ((u64)device->card->node_id << OWNER_NODE_SHIFT) | 283 dice->notification_handler.offset); 284 buffer[1] = cpu_to_be64(OWNER_NO_OWNER); 285 snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP, 286 get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL, 287 GLOBAL_OWNER), 288 buffer, 2 * 8, FW_QUIET | 289 FW_FIXED_GENERATION | dice->owner_generation); 290 291 kfree(buffer); 292 293 dice->owner_generation = -1; 294 } 295 296 void snd_dice_transaction_destroy(struct snd_dice *dice) 297 { 298 struct fw_address_handler *handler = &dice->notification_handler; 299 300 if (handler->callback_data == NULL) 301 return; 302 303 unregister_notification_address(dice); 304 305 fw_core_remove_address_handler(handler); 306 handler->callback_data = NULL; 307 } 308 309 int snd_dice_transaction_reinit(struct snd_dice *dice) 310 { 311 struct fw_address_handler *handler = &dice->notification_handler; 312 313 if (handler->callback_data == NULL) 314 return -EINVAL; 315 316 return register_notification_address(dice, false); 317 } 318 319 static int get_subaddrs(struct snd_dice *dice) 320 { 321 static const int min_values[10] = { 322 10, 0x64 / 4, 323 10, 0x18 / 4, 324 10, 0x18 / 4, 325 0, 0, 326 0, 0, 327 }; 328 __be32 *pointers; 329 __be32 version; 330 u32 data; 331 unsigned int i; 332 int err; 333 334 pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32), 335 GFP_KERNEL); 336 if (pointers == NULL) 337 return -ENOMEM; 338 339 /* 340 * Check that the sub address spaces exist and are located inside the 341 * private address space. The minimum values are chosen so that all 342 * minimally required registers are included. 343 */ 344 err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST, 345 DICE_PRIVATE_SPACE, pointers, 346 sizeof(__be32) * ARRAY_SIZE(min_values), 0); 347 if (err < 0) 348 goto end; 349 350 for (i = 0; i < ARRAY_SIZE(min_values); ++i) { 351 data = be32_to_cpu(pointers[i]); 352 if (data < min_values[i] || data >= 0x40000) { 353 err = -ENODEV; 354 goto end; 355 } 356 } 357 358 /* 359 * Check that the implemented DICE driver specification major version 360 * number matches. 361 */ 362 err = snd_fw_transaction(dice->unit, TCODE_READ_QUADLET_REQUEST, 363 DICE_PRIVATE_SPACE + 364 be32_to_cpu(pointers[0]) * 4 + GLOBAL_VERSION, 365 &version, sizeof(version), 0); 366 if (err < 0) 367 goto end; 368 369 if ((version & cpu_to_be32(0xff000000)) != cpu_to_be32(0x01000000)) { 370 dev_err(&dice->unit->device, 371 "unknown DICE version: 0x%08x\n", be32_to_cpu(version)); 372 err = -ENODEV; 373 goto end; 374 } 375 376 dice->global_offset = be32_to_cpu(pointers[0]) * 4; 377 dice->tx_offset = be32_to_cpu(pointers[2]) * 4; 378 dice->rx_offset = be32_to_cpu(pointers[4]) * 4; 379 dice->sync_offset = be32_to_cpu(pointers[6]) * 4; 380 dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4; 381 382 /* Set up later. */ 383 if (be32_to_cpu(pointers[1]) * 4 >= GLOBAL_CLOCK_CAPABILITIES + 4) 384 dice->clock_caps = 1; 385 end: 386 kfree(pointers); 387 return err; 388 } 389 390 int snd_dice_transaction_init(struct snd_dice *dice) 391 { 392 struct fw_address_handler *handler = &dice->notification_handler; 393 int err; 394 395 err = get_subaddrs(dice); 396 if (err < 0) 397 return err; 398 399 /* Allocation callback in address space over host controller */ 400 handler->length = 4; 401 handler->address_callback = dice_notification; 402 handler->callback_data = dice; 403 err = fw_core_add_address_handler(handler, &fw_high_memory_region); 404 if (err < 0) { 405 handler->callback_data = NULL; 406 return err; 407 } 408 409 /* Register the address space */ 410 err = register_notification_address(dice, true); 411 if (err < 0) { 412 fw_core_remove_address_handler(handler); 413 handler->callback_data = NULL; 414 } 415 416 return err; 417 } 418