linux/sound/firewire/dice/dice-transaction.c
<<
>>
Prefs
   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
  12static 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
  37int 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
  47int 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
  57static 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
  63int 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
  76int 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];
  93end:
  94        return err;
  95}
  96
  97int 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;
 115end:
 116        return err;
 117}
 118
 119void 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
 133static 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
 164static 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
 218static 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
 242void 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
 255int 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
 265static 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;
 338end:
 339        kfree(pointers);
 340        return err;
 341}
 342
 343int 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