123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * dice-extension.c - a part of driver for DICE based devices
- *
- * Copyright (c) 2018 Takashi Sakamoto
- */
- #include "dice.h"
- /* For TCD2210/2220, TCAT defines extension of application protocol. */
- #define DICE_EXT_APP_SPACE 0xffffe0200000uLL
- #define DICE_EXT_APP_CAPS_OFFSET 0x00
- #define DICE_EXT_APP_CAPS_SIZE 0x04
- #define DICE_EXT_APP_CMD_OFFSET 0x08
- #define DICE_EXT_APP_CMD_SIZE 0x0c
- #define DICE_EXT_APP_MIXER_OFFSET 0x10
- #define DICE_EXT_APP_MIXER_SIZE 0x14
- #define DICE_EXT_APP_PEAK_OFFSET 0x18
- #define DICE_EXT_APP_PEAK_SIZE 0x1c
- #define DICE_EXT_APP_ROUTER_OFFSET 0x20
- #define DICE_EXT_APP_ROUTER_SIZE 0x24
- #define DICE_EXT_APP_STREAM_OFFSET 0x28
- #define DICE_EXT_APP_STREAM_SIZE 0x2c
- #define DICE_EXT_APP_CURRENT_OFFSET 0x30
- #define DICE_EXT_APP_CURRENT_SIZE 0x34
- #define DICE_EXT_APP_STANDALONE_OFFSET 0x38
- #define DICE_EXT_APP_STANDALONE_SIZE 0x3c
- #define DICE_EXT_APP_APPLICATION_OFFSET 0x40
- #define DICE_EXT_APP_APPLICATION_SIZE 0x44
- #define EXT_APP_STREAM_TX_NUMBER 0x0000
- #define EXT_APP_STREAM_RX_NUMBER 0x0004
- #define EXT_APP_STREAM_ENTRIES 0x0008
- #define EXT_APP_STREAM_ENTRY_SIZE 0x010c
- #define EXT_APP_NUMBER_AUDIO 0x0000
- #define EXT_APP_NUMBER_MIDI 0x0004
- #define EXT_APP_NAMES 0x0008
- #define EXT_APP_NAMES_SIZE 256
- #define EXT_APP_AC3 0x0108
- #define EXT_APP_CONFIG_LOW_ROUTER 0x0000
- #define EXT_APP_CONFIG_LOW_STREAM 0x1000
- #define EXT_APP_CONFIG_MIDDLE_ROUTER 0x2000
- #define EXT_APP_CONFIG_MIDDLE_STREAM 0x3000
- #define EXT_APP_CONFIG_HIGH_ROUTER 0x4000
- #define EXT_APP_CONFIG_HIGH_STREAM 0x5000
- static inline int read_transaction(struct snd_dice *dice, u64 section_addr,
- u32 offset, void *buf, size_t len)
- {
- return snd_fw_transaction(dice->unit,
- len == 4 ? TCODE_READ_QUADLET_REQUEST :
- TCODE_READ_BLOCK_REQUEST,
- section_addr + offset, buf, len, 0);
- }
- static int read_stream_entries(struct snd_dice *dice, u64 section_addr,
- u32 base_offset, unsigned int stream_count,
- unsigned int mode,
- unsigned int pcm_channels[MAX_STREAMS][3],
- unsigned int midi_ports[MAX_STREAMS])
- {
- u32 entry_offset;
- __be32 reg[2];
- int err;
- int i;
- for (i = 0; i < stream_count; ++i) {
- entry_offset = base_offset + i * EXT_APP_STREAM_ENTRY_SIZE;
- err = read_transaction(dice, section_addr,
- entry_offset + EXT_APP_NUMBER_AUDIO,
- reg, sizeof(reg));
- if (err < 0)
- return err;
- pcm_channels[i][mode] = be32_to_cpu(reg[0]);
- midi_ports[i] = max(midi_ports[i], be32_to_cpu(reg[1]));
- }
- return 0;
- }
- static int detect_stream_formats(struct snd_dice *dice, u64 section_addr)
- {
- u32 base_offset;
- __be32 reg[2];
- unsigned int stream_count;
- int mode;
- int err = 0;
- for (mode = 0; mode < SND_DICE_RATE_MODE_COUNT; ++mode) {
- unsigned int cap;
- /*
- * Some models report stream formats at highest mode, however
- * they don't support the mode. Check clock capabilities.
- */
- if (mode == 2) {
- cap = CLOCK_CAP_RATE_176400 | CLOCK_CAP_RATE_192000;
- } else if (mode == 1) {
- cap = CLOCK_CAP_RATE_88200 | CLOCK_CAP_RATE_96000;
- } else {
- cap = CLOCK_CAP_RATE_32000 | CLOCK_CAP_RATE_44100 |
- CLOCK_CAP_RATE_48000;
- }
- if (!(cap & dice->clock_caps))
- continue;
- base_offset = 0x2000 * mode + 0x1000;
- err = read_transaction(dice, section_addr,
- base_offset + EXT_APP_STREAM_TX_NUMBER,
- ®, sizeof(reg));
- if (err < 0)
- break;
- base_offset += EXT_APP_STREAM_ENTRIES;
- stream_count = be32_to_cpu(reg[0]);
- err = read_stream_entries(dice, section_addr, base_offset,
- stream_count, mode,
- dice->tx_pcm_chs,
- dice->tx_midi_ports);
- if (err < 0)
- break;
- base_offset += stream_count * EXT_APP_STREAM_ENTRY_SIZE;
- stream_count = be32_to_cpu(reg[1]);
- err = read_stream_entries(dice, section_addr, base_offset,
- stream_count,
- mode, dice->rx_pcm_chs,
- dice->rx_midi_ports);
- if (err < 0)
- break;
- }
- return err;
- }
- int snd_dice_detect_extension_formats(struct snd_dice *dice)
- {
- __be32 *pointers;
- unsigned int i;
- u64 section_addr;
- int err;
- pointers = kmalloc_array(9, sizeof(__be32) * 2, GFP_KERNEL);
- if (pointers == NULL)
- return -ENOMEM;
- err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
- DICE_EXT_APP_SPACE, pointers,
- 9 * sizeof(__be32) * 2, 0);
- if (err < 0)
- goto end;
- /* Check two of them for offset have the same value or not. */
- for (i = 0; i < 9; ++i) {
- int j;
- for (j = i + 1; j < 9; ++j) {
- if (pointers[i * 2] == pointers[j * 2]) {
- // Fallback to limited functionality.
- err = -ENXIO;
- goto end;
- }
- }
- }
- section_addr = DICE_EXT_APP_SPACE + be32_to_cpu(pointers[12]) * 4;
- err = detect_stream_formats(dice, section_addr);
- end:
- kfree(pointers);
- return err;
- }
|