root/sound/firewire/dice/dice-transaction.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. get_subaddr
  2. snd_dice_transaction_write
  3. snd_dice_transaction_read
  4. get_clock_info
  5. snd_dice_transaction_get_clock_source
  6. snd_dice_transaction_get_rate
  7. snd_dice_transaction_set_enable
  8. snd_dice_transaction_clear_enable
  9. dice_notification
  10. register_notification_address
  11. unregister_notification_address
  12. snd_dice_transaction_destroy
  13. snd_dice_transaction_reinit
  14. get_subaddrs
  15. snd_dice_transaction_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * dice_transaction.c - a part of driver for Dice based devices
   4  *
   5  * Copyright (c) Clemens Ladisch
   6  * Copyright (c) 2014 Takashi Sakamoto
   7  */
   8 
   9 #include "dice.h"
  10 
  11 static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
  12                        u64 offset)
  13 {
  14         switch (type) {
  15         case SND_DICE_ADDR_TYPE_TX:
  16                 offset += dice->tx_offset;
  17                 break;
  18         case SND_DICE_ADDR_TYPE_RX:
  19                 offset += dice->rx_offset;
  20                 break;
  21         case SND_DICE_ADDR_TYPE_SYNC:
  22                 offset += dice->sync_offset;
  23                 break;
  24         case SND_DICE_ADDR_TYPE_RSRV:
  25                 offset += dice->rsrv_offset;
  26                 break;
  27         case SND_DICE_ADDR_TYPE_GLOBAL:
  28         default:
  29                 offset += dice->global_offset;
  30                 break;
  31         }
  32         offset += DICE_PRIVATE_SPACE;
  33         return offset;
  34 }
  35 
  36 int snd_dice_transaction_write(struct snd_dice *dice,
  37                                enum snd_dice_addr_type type,
  38                                unsigned int offset, void *buf, unsigned int len)
  39 {
  40         return snd_fw_transaction(dice->unit,
  41                                   (len == 4) ? TCODE_WRITE_QUADLET_REQUEST :
  42                                                TCODE_WRITE_BLOCK_REQUEST,
  43                                   get_subaddr(dice, type, offset), buf, len, 0);
  44 }
  45 
  46 int snd_dice_transaction_read(struct snd_dice *dice,
  47                               enum snd_dice_addr_type type, unsigned int offset,
  48                               void *buf, unsigned int len)
  49 {
  50         return snd_fw_transaction(dice->unit,
  51                                   (len == 4) ? TCODE_READ_QUADLET_REQUEST :
  52                                                TCODE_READ_BLOCK_REQUEST,
  53                                   get_subaddr(dice, type, offset), buf, len, 0);
  54 }
  55 
  56 static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
  57 {
  58         return snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
  59                                                 info, 4);
  60 }
  61 
  62 int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
  63                                           unsigned int *source)
  64 {
  65         __be32 info;
  66         int err;
  67 
  68         err = get_clock_info(dice, &info);
  69         if (err >= 0)
  70                 *source = be32_to_cpu(info) & CLOCK_SOURCE_MASK;
  71 
  72         return err;
  73 }
  74 
  75 int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
  76 {
  77         __be32 info;
  78         unsigned int index;
  79         int err;
  80 
  81         err = get_clock_info(dice, &info);
  82         if (err < 0)
  83                 goto end;
  84 
  85         index = (be32_to_cpu(info) & CLOCK_RATE_MASK) >> CLOCK_RATE_SHIFT;
  86         if (index >= SND_DICE_RATES_COUNT) {
  87                 err = -ENOSYS;
  88                 goto end;
  89         }
  90 
  91         *rate = snd_dice_rates[index];
  92 end:
  93         return err;
  94 }
  95 
  96 int snd_dice_transaction_set_enable(struct snd_dice *dice)
  97 {
  98         __be32 value;
  99         int err = 0;
 100 
 101         if (dice->global_enabled)
 102                 goto end;
 103 
 104         value = cpu_to_be32(1);
 105         err = snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
 106                                  get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
 107                                              GLOBAL_ENABLE),
 108                                  &value, 4,
 109                                  FW_FIXED_GENERATION | dice->owner_generation);
 110         if (err < 0)
 111                 goto end;
 112 
 113         dice->global_enabled = true;
 114 end:
 115         return err;
 116 }
 117 
 118 void snd_dice_transaction_clear_enable(struct snd_dice *dice)
 119 {
 120         __be32 value;
 121 
 122         value = 0;
 123         snd_fw_transaction(dice->unit, TCODE_WRITE_QUADLET_REQUEST,
 124                            get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
 125                                        GLOBAL_ENABLE),
 126                            &value, 4, FW_QUIET |
 127                            FW_FIXED_GENERATION | dice->owner_generation);
 128 
 129         dice->global_enabled = false;
 130 }
 131 
 132 static void dice_notification(struct fw_card *card, struct fw_request *request,
 133                               int tcode, int destination, int source,
 134                               int generation, unsigned long long offset,
 135                               void *data, size_t length, void *callback_data)
 136 {
 137         struct snd_dice *dice = callback_data;
 138         u32 bits;
 139         unsigned long flags;
 140 
 141         if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
 142                 fw_send_response(card, request, RCODE_TYPE_ERROR);
 143                 return;
 144         }
 145         if ((offset & 3) != 0) {
 146                 fw_send_response(card, request, RCODE_ADDRESS_ERROR);
 147                 return;
 148         }
 149 
 150         bits = be32_to_cpup(data);
 151 
 152         spin_lock_irqsave(&dice->lock, flags);
 153         dice->notification_bits |= bits;
 154         spin_unlock_irqrestore(&dice->lock, flags);
 155 
 156         fw_send_response(card, request, RCODE_COMPLETE);
 157 
 158         if (bits & NOTIFY_LOCK_CHG)
 159                 complete(&dice->clock_accepted);
 160         wake_up(&dice->hwdep_wait);
 161 }
 162 
 163 static int register_notification_address(struct snd_dice *dice, bool retry)
 164 {
 165         struct fw_device *device = fw_parent_device(dice->unit);
 166         __be64 *buffer;
 167         unsigned int retries;
 168         int err;
 169 
 170         retries = (retry) ? 3 : 0;
 171 
 172         buffer = kmalloc(2 * 8, GFP_KERNEL);
 173         if (!buffer)
 174                 return -ENOMEM;
 175 
 176         for (;;) {
 177                 buffer[0] = cpu_to_be64(OWNER_NO_OWNER);
 178                 buffer[1] = cpu_to_be64(
 179                         ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
 180                         dice->notification_handler.offset);
 181 
 182                 dice->owner_generation = device->generation;
 183                 smp_rmb(); /* node_id vs. generation */
 184                 err = snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
 185                                          get_subaddr(dice,
 186                                                      SND_DICE_ADDR_TYPE_GLOBAL,
 187                                                      GLOBAL_OWNER),
 188                                          buffer, 2 * 8,
 189                                          FW_FIXED_GENERATION |
 190                                                         dice->owner_generation);
 191                 if (err == 0) {
 192                         /* success */
 193                         if (buffer[0] == cpu_to_be64(OWNER_NO_OWNER))
 194                                 break;
 195                         /* The address seems to be already registered. */
 196                         if (buffer[0] == buffer[1])
 197                                 break;
 198 
 199                         dev_err(&dice->unit->device,
 200                                 "device is already in use\n");
 201                         err = -EBUSY;
 202                 }
 203                 if (err != -EAGAIN || retries-- > 0)
 204                         break;
 205 
 206                 msleep(20);
 207         }
 208 
 209         kfree(buffer);
 210 
 211         if (err < 0)
 212                 dice->owner_generation = -1;
 213 
 214         return err;
 215 }
 216 
 217 static void unregister_notification_address(struct snd_dice *dice)
 218 {
 219         struct fw_device *device = fw_parent_device(dice->unit);
 220         __be64 *buffer;
 221 
 222         buffer = kmalloc(2 * 8, GFP_KERNEL);
 223         if (buffer == NULL)
 224                 return;
 225 
 226         buffer[0] = cpu_to_be64(
 227                 ((u64)device->card->node_id << OWNER_NODE_SHIFT) |
 228                 dice->notification_handler.offset);
 229         buffer[1] = cpu_to_be64(OWNER_NO_OWNER);
 230         snd_fw_transaction(dice->unit, TCODE_LOCK_COMPARE_SWAP,
 231                            get_subaddr(dice, SND_DICE_ADDR_TYPE_GLOBAL,
 232                                        GLOBAL_OWNER),
 233                            buffer, 2 * 8, FW_QUIET |
 234                            FW_FIXED_GENERATION | dice->owner_generation);
 235 
 236         kfree(buffer);
 237 
 238         dice->owner_generation = -1;
 239 }
 240 
 241 void snd_dice_transaction_destroy(struct snd_dice *dice)
 242 {
 243         struct fw_address_handler *handler = &dice->notification_handler;
 244 
 245         if (handler->callback_data == NULL)
 246                 return;
 247 
 248         unregister_notification_address(dice);
 249 
 250         fw_core_remove_address_handler(handler);
 251         handler->callback_data = NULL;
 252 }
 253 
 254 int snd_dice_transaction_reinit(struct snd_dice *dice)
 255 {
 256         struct fw_address_handler *handler = &dice->notification_handler;
 257 
 258         if (handler->callback_data == NULL)
 259                 return -EINVAL;
 260 
 261         return register_notification_address(dice, false);
 262 }
 263 
 264 static int get_subaddrs(struct snd_dice *dice)
 265 {
 266         static const int min_values[10] = {
 267                 10, 0x60 / 4,
 268                 10, 0x18 / 4,
 269                 10, 0x18 / 4,
 270                 0, 0,
 271                 0, 0,
 272         };
 273         __be32 *pointers;
 274         __be32 version;
 275         u32 data;
 276         unsigned int i;
 277         int err;
 278 
 279         pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
 280                                  GFP_KERNEL);
 281         if (pointers == NULL)
 282                 return -ENOMEM;
 283 
 284         /*
 285          * Check that the sub address spaces exist and are located inside the
 286          * private address space.  The minimum values are chosen so that all
 287          * minimally required registers are included.
 288          */
 289         err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
 290                                  DICE_PRIVATE_SPACE, pointers,
 291                                  sizeof(__be32) * ARRAY_SIZE(min_values), 0);
 292         if (err < 0)
 293                 goto end;
 294 
 295         for (i = 0; i < ARRAY_SIZE(min_values); ++i) {
 296                 data = be32_to_cpu(pointers[i]);
 297                 if (data < min_values[i] || data >= 0x40000) {
 298                         err = -ENODEV;
 299                         goto end;
 300                 }
 301         }
 302 
 303         if (be32_to_cpu(pointers[1]) > 0x18) {
 304                 /*
 305                  * Check that the implemented DICE driver specification major
 306                  * version 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)) !=
 316                                                 cpu_to_be32(0x01000000)) {
 317                         dev_err(&dice->unit->device,
 318                                 "unknown DICE version: 0x%08x\n",
 319                                 be32_to_cpu(version));
 320                         err = -ENODEV;
 321                         goto end;
 322                 }
 323 
 324                 /* Set up later. */
 325                 dice->clock_caps = 1;
 326         }
 327 
 328         dice->global_offset = be32_to_cpu(pointers[0]) * 4;
 329         dice->tx_offset = be32_to_cpu(pointers[2]) * 4;
 330         dice->rx_offset = be32_to_cpu(pointers[4]) * 4;
 331 
 332         /* Old firmware doesn't support these fields. */
 333         if (pointers[7])
 334                 dice->sync_offset = be32_to_cpu(pointers[6]) * 4;
 335         if (pointers[9])
 336                 dice->rsrv_offset = be32_to_cpu(pointers[8]) * 4;
 337 end:
 338         kfree(pointers);
 339         return err;
 340 }
 341 
 342 int snd_dice_transaction_init(struct snd_dice *dice)
 343 {
 344         struct fw_address_handler *handler = &dice->notification_handler;
 345         int err;
 346 
 347         err = get_subaddrs(dice);
 348         if (err < 0)
 349                 return err;
 350 
 351         /* Allocation callback in address space over host controller */
 352         handler->length = 4;
 353         handler->address_callback = dice_notification;
 354         handler->callback_data = dice;
 355         err = fw_core_add_address_handler(handler, &fw_high_memory_region);
 356         if (err < 0) {
 357                 handler->callback_data = NULL;
 358                 return err;
 359         }
 360 
 361         /* Register the address space */
 362         err = register_notification_address(dice, true);
 363         if (err < 0) {
 364                 fw_core_remove_address_handler(handler);
 365                 handler->callback_data = NULL;
 366         }
 367 
 368         return err;
 369 }

/* [<][>][^][v][top][bottom][index][help] */