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(unit: dice->unit,
54 tcode: len == 4 ? TCODE_READ_QUADLET_REQUEST :
55 TCODE_READ_BLOCK_REQUEST,
56 offset: section_addr + offset, buffer: buf, length: len, flags: 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 offset: entry_offset + EXT_APP_NUMBER_AUDIO,
74 buf: reg, len: 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 offset: base_offset + EXT_APP_STREAM_TX_NUMBER,
114 buf: &reg, len: 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 pcm_channels: dice->tx_pcm_chs,
123 midi_ports: 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, pcm_channels: dice->rx_pcm_chs,
132 midi_ports: 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(n: 9, size: sizeof(__be32) * 2, GFP_KERNEL);
148 if (pointers == NULL)
149 return -ENOMEM;
150
151 err = snd_fw_transaction(unit: dice->unit, TCODE_READ_BLOCK_REQUEST,
152 DICE_EXT_APP_SPACE, buffer: pointers,
153 length: 9 * sizeof(__be32) * 2, flags: 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(objp: pointers);
174 return err;
175}
176

source code of linux/sound/firewire/dice/dice-extension.c