linux/sound/firewire/dice/dice-extension.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * dice-extension.c - a part of driver for DICE based devices
   4 *
   5 * Copyright (c) 2018 Takashi Sakamoto
   6 */
   7
   8#include "dice.h"
   9
  10/* For TCD2210/2220, TCAT defines extension of application protocol. */
  11
  12#define DICE_EXT_APP_SPACE              0xffffe0200000uLL
  13
  14#define DICE_EXT_APP_CAPS_OFFSET        0x00
  15#define DICE_EXT_APP_CAPS_SIZE          0x04
  16#define DICE_EXT_APP_CMD_OFFSET         0x08
  17#define DICE_EXT_APP_CMD_SIZE           0x0c
  18#define DICE_EXT_APP_MIXER_OFFSET       0x10
  19#define DICE_EXT_APP_MIXER_SIZE         0x14
  20#define DICE_EXT_APP_PEAK_OFFSET        0x18
  21#define DICE_EXT_APP_PEAK_SIZE          0x1c
  22#define DICE_EXT_APP_ROUTER_OFFSET      0x20
  23#define DICE_EXT_APP_ROUTER_SIZE        0x24
  24#define DICE_EXT_APP_STREAM_OFFSET      0x28
  25#define DICE_EXT_APP_STREAM_SIZE        0x2c
  26#define DICE_EXT_APP_CURRENT_OFFSET     0x30
  27#define DICE_EXT_APP_CURRENT_SIZE       0x34
  28#define DICE_EXT_APP_STANDALONE_OFFSET  0x38
  29#define DICE_EXT_APP_STANDALONE_SIZE    0x3c
  30#define DICE_EXT_APP_APPLICATION_OFFSET 0x40
  31#define DICE_EXT_APP_APPLICATION_SIZE   0x44
  32
  33#define EXT_APP_STREAM_TX_NUMBER        0x0000
  34#define EXT_APP_STREAM_RX_NUMBER        0x0004
  35#define EXT_APP_STREAM_ENTRIES          0x0008
  36#define EXT_APP_STREAM_ENTRY_SIZE       0x010c
  37#define  EXT_APP_NUMBER_AUDIO           0x0000
  38#define  EXT_APP_NUMBER_MIDI            0x0004
  39#define  EXT_APP_NAMES                  0x0008
  40#define   EXT_APP_NAMES_SIZE            256
  41#define  EXT_APP_AC3                    0x0108
  42
  43#define EXT_APP_CONFIG_LOW_ROUTER       0x0000
  44#define EXT_APP_CONFIG_LOW_STREAM       0x1000
  45#define EXT_APP_CONFIG_MIDDLE_ROUTER    0x2000
  46#define EXT_APP_CONFIG_MIDDLE_STREAM    0x3000
  47#define EXT_APP_CONFIG_HIGH_ROUTER      0x4000
  48#define EXT_APP_CONFIG_HIGH_STREAM      0x5000
  49
  50static inline int read_transaction(struct snd_dice *dice, u64 section_addr,
  51                                   u32 offset, void *buf, size_t len)
  52{
  53        return snd_fw_transaction(dice->unit,
  54                                  len == 4 ? TCODE_READ_QUADLET_REQUEST :
  55                                             TCODE_READ_BLOCK_REQUEST,
  56                                  section_addr + offset, buf, len, 0);
  57}
  58
  59static int read_stream_entries(struct snd_dice *dice, u64 section_addr,
  60                               u32 base_offset, unsigned int stream_count,
  61                               unsigned int mode,
  62                               unsigned int pcm_channels[MAX_STREAMS][3],
  63                               unsigned int midi_ports[MAX_STREAMS])
  64{
  65        u32 entry_offset;
  66        __be32 reg[2];
  67        int err;
  68        int i;
  69
  70        for (i = 0; i < stream_count; ++i) {
  71                entry_offset = base_offset + i * EXT_APP_STREAM_ENTRY_SIZE;
  72                err = read_transaction(dice, section_addr,
  73                                    entry_offset + EXT_APP_NUMBER_AUDIO,
  74                                    reg, sizeof(reg));
  75                if (err < 0)
  76                        return err;
  77                pcm_channels[i][mode] = be32_to_cpu(reg[0]);
  78                midi_ports[i] = max(midi_ports[i], be32_to_cpu(reg[1]));
  79        }
  80
  81        return 0;
  82}
  83
  84static int detect_stream_formats(struct snd_dice *dice, u64 section_addr)
  85{
  86        u32 base_offset;
  87        __be32 reg[2];
  88        unsigned int stream_count;
  89        int mode;
  90        int err = 0;
  91
  92        for (mode = 0; mode < SND_DICE_RATE_MODE_COUNT; ++mode) {
  93                unsigned int cap;
  94
  95                /*
  96                 * Some models report stream formats at highest mode, however
  97                 * they don't support the mode. Check clock capabilities.
  98                 */
  99                if (mode == 2) {
 100                        cap = CLOCK_CAP_RATE_176400 | CLOCK_CAP_RATE_192000;
 101                } else if (mode == 1) {
 102                        cap = CLOCK_CAP_RATE_88200 | CLOCK_CAP_RATE_96000;
 103                } else {
 104                        cap = CLOCK_CAP_RATE_32000 | CLOCK_CAP_RATE_44100 |
 105                              CLOCK_CAP_RATE_48000;
 106                }
 107                if (!(cap & dice->clock_caps))
 108                        continue;
 109
 110                base_offset = 0x2000 * mode + 0x1000;
 111
 112                err = read_transaction(dice, section_addr,
 113                                       base_offset + EXT_APP_STREAM_TX_NUMBER,
 114                                       &reg, sizeof(reg));
 115                if (err < 0)
 116                        break;
 117
 118                base_offset += EXT_APP_STREAM_ENTRIES;
 119                stream_count = be32_to_cpu(reg[0]);
 120                err = read_stream_entries(dice, section_addr, base_offset,
 121                                          stream_count, mode,
 122                                          dice->tx_pcm_chs,
 123                                          dice->tx_midi_ports);
 124                if (err < 0)
 125                        break;
 126
 127                base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE;
 128                stream_count = be32_to_cpu(reg[1]);
 129                err = read_stream_entries(dice, section_addr, base_offset,
 130                                          stream_count,
 131                                          mode, dice->rx_pcm_chs,
 132                                          dice->rx_midi_ports);
 133                if (err < 0)
 134                        break;
 135        }
 136
 137        return err;
 138}
 139
 140int snd_dice_detect_extension_formats(struct snd_dice *dice)
 141{
 142        __be32 *pointers;
 143        unsigned int i;
 144        u64 section_addr;
 145        int err;
 146
 147        pointers = kmalloc_array(9, sizeof(__be32) * 2, GFP_KERNEL);
 148        if (pointers == NULL)
 149                return -ENOMEM;
 150
 151        err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
 152                                 DICE_EXT_APP_SPACE, pointers,
 153                                 9 * sizeof(__be32) * 2, 0);
 154        if (err < 0)
 155                goto end;
 156
 157        /* Check two of them for offset have the same value or not. */
 158        for (i = 0; i < 9; ++i) {
 159                int j;
 160
 161                for (j = i + 1; j < 9; ++j) {
 162                        if (pointers[i * 2] == pointers[j * 2]) {
 163                                // Fallback to limited functionality.
 164                                err = -ENXIO;
 165                                goto end;
 166                        }
 167                }
 168        }
 169
 170        section_addr = DICE_EXT_APP_SPACE + be32_to_cpu(pointers[12]) * 4;
 171        err = detect_stream_formats(dice, section_addr);
 172end:
 173        kfree(pointers);
 174        return err;
 175}
 176