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