dice-extension.c (4776B)
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 ®, 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}