linux/sound/firewire/dice/dice-transaction.c
<<
>>
Prefs
   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
  11static 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
  36int 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
  46int 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
  56static 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
  62int 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
  75int 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];
  92end:
  93        return err;
  94}
  95
  96int 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;
 114end:
 115        return err;
 116}
 117
 118void 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
 132static 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
 163static 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
 217static 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
 241void 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
 254int 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
 264static 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;
 337end:
 338        kfree(pointers);
 339        return err;
 340}
 341
 342int 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}
 370