1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * dice_proc.c - a part of driver for Dice based devices
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) Clemens Ladisch
6*4882a593Smuzhiyun * Copyright (c) 2014 Takashi Sakamoto
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include "dice.h"
10*4882a593Smuzhiyun
dice_proc_read_mem(struct snd_dice * dice,void * buffer,unsigned int offset_q,unsigned int quadlets)11*4882a593Smuzhiyun static int dice_proc_read_mem(struct snd_dice *dice, void *buffer,
12*4882a593Smuzhiyun unsigned int offset_q, unsigned int quadlets)
13*4882a593Smuzhiyun {
14*4882a593Smuzhiyun unsigned int i;
15*4882a593Smuzhiyun int err;
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun err = snd_fw_transaction(dice->unit, TCODE_READ_BLOCK_REQUEST,
18*4882a593Smuzhiyun DICE_PRIVATE_SPACE + 4 * offset_q,
19*4882a593Smuzhiyun buffer, 4 * quadlets, 0);
20*4882a593Smuzhiyun if (err < 0)
21*4882a593Smuzhiyun return err;
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun for (i = 0; i < quadlets; ++i)
24*4882a593Smuzhiyun be32_to_cpus(&((u32 *)buffer)[i]);
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun return 0;
27*4882a593Smuzhiyun }
28*4882a593Smuzhiyun
str_from_array(const char * const strs[],unsigned int count,unsigned int i)29*4882a593Smuzhiyun static const char *str_from_array(const char *const strs[], unsigned int count,
30*4882a593Smuzhiyun unsigned int i)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun if (i < count)
33*4882a593Smuzhiyun return strs[i];
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun return "(unknown)";
36*4882a593Smuzhiyun }
37*4882a593Smuzhiyun
dice_proc_fixup_string(char * s,unsigned int size)38*4882a593Smuzhiyun static void dice_proc_fixup_string(char *s, unsigned int size)
39*4882a593Smuzhiyun {
40*4882a593Smuzhiyun unsigned int i;
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun for (i = 0; i < size; i += 4)
43*4882a593Smuzhiyun cpu_to_le32s((u32 *)(s + i));
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun for (i = 0; i < size - 2; ++i) {
46*4882a593Smuzhiyun if (s[i] == '\0')
47*4882a593Smuzhiyun return;
48*4882a593Smuzhiyun if (s[i] == '\\' && s[i + 1] == '\\') {
49*4882a593Smuzhiyun s[i + 2] = '\0';
50*4882a593Smuzhiyun return;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun s[size - 1] = '\0';
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun
dice_proc_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)56*4882a593Smuzhiyun static void dice_proc_read(struct snd_info_entry *entry,
57*4882a593Smuzhiyun struct snd_info_buffer *buffer)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun static const char *const section_names[5] = {
60*4882a593Smuzhiyun "global", "tx", "rx", "ext_sync", "unused2"
61*4882a593Smuzhiyun };
62*4882a593Smuzhiyun static const char *const clock_sources[] = {
63*4882a593Smuzhiyun "aes1", "aes2", "aes3", "aes4", "aes", "adat", "tdif",
64*4882a593Smuzhiyun "wc", "arx1", "arx2", "arx3", "arx4", "internal"
65*4882a593Smuzhiyun };
66*4882a593Smuzhiyun static const char *const rates[] = {
67*4882a593Smuzhiyun "32000", "44100", "48000", "88200", "96000", "176400", "192000",
68*4882a593Smuzhiyun "any low", "any mid", "any high", "none"
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun struct snd_dice *dice = entry->private_data;
71*4882a593Smuzhiyun u32 sections[ARRAY_SIZE(section_names) * 2];
72*4882a593Smuzhiyun struct {
73*4882a593Smuzhiyun u32 number;
74*4882a593Smuzhiyun u32 size;
75*4882a593Smuzhiyun } tx_rx_header;
76*4882a593Smuzhiyun union {
77*4882a593Smuzhiyun struct {
78*4882a593Smuzhiyun u32 owner_hi, owner_lo;
79*4882a593Smuzhiyun u32 notification;
80*4882a593Smuzhiyun char nick_name[NICK_NAME_SIZE];
81*4882a593Smuzhiyun u32 clock_select;
82*4882a593Smuzhiyun u32 enable;
83*4882a593Smuzhiyun u32 status;
84*4882a593Smuzhiyun u32 extended_status;
85*4882a593Smuzhiyun u32 sample_rate;
86*4882a593Smuzhiyun u32 version;
87*4882a593Smuzhiyun u32 clock_caps;
88*4882a593Smuzhiyun char clock_source_names[CLOCK_SOURCE_NAMES_SIZE];
89*4882a593Smuzhiyun } global;
90*4882a593Smuzhiyun struct {
91*4882a593Smuzhiyun u32 iso;
92*4882a593Smuzhiyun u32 number_audio;
93*4882a593Smuzhiyun u32 number_midi;
94*4882a593Smuzhiyun u32 speed;
95*4882a593Smuzhiyun char names[TX_NAMES_SIZE];
96*4882a593Smuzhiyun u32 ac3_caps;
97*4882a593Smuzhiyun u32 ac3_enable;
98*4882a593Smuzhiyun } tx;
99*4882a593Smuzhiyun struct {
100*4882a593Smuzhiyun u32 iso;
101*4882a593Smuzhiyun u32 seq_start;
102*4882a593Smuzhiyun u32 number_audio;
103*4882a593Smuzhiyun u32 number_midi;
104*4882a593Smuzhiyun char names[RX_NAMES_SIZE];
105*4882a593Smuzhiyun u32 ac3_caps;
106*4882a593Smuzhiyun u32 ac3_enable;
107*4882a593Smuzhiyun } rx;
108*4882a593Smuzhiyun struct {
109*4882a593Smuzhiyun u32 clock_source;
110*4882a593Smuzhiyun u32 locked;
111*4882a593Smuzhiyun u32 rate;
112*4882a593Smuzhiyun u32 adat_user_data;
113*4882a593Smuzhiyun } ext_sync;
114*4882a593Smuzhiyun } buf;
115*4882a593Smuzhiyun unsigned int quadlets, stream, i;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun if (dice_proc_read_mem(dice, sections, 0, ARRAY_SIZE(sections)) < 0)
118*4882a593Smuzhiyun return;
119*4882a593Smuzhiyun snd_iprintf(buffer, "sections:\n");
120*4882a593Smuzhiyun for (i = 0; i < ARRAY_SIZE(section_names); ++i)
121*4882a593Smuzhiyun snd_iprintf(buffer, " %s: offset %u, size %u\n",
122*4882a593Smuzhiyun section_names[i],
123*4882a593Smuzhiyun sections[i * 2], sections[i * 2 + 1]);
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun quadlets = min_t(u32, sections[1], sizeof(buf.global) / 4);
126*4882a593Smuzhiyun if (dice_proc_read_mem(dice, &buf.global, sections[0], quadlets) < 0)
127*4882a593Smuzhiyun return;
128*4882a593Smuzhiyun snd_iprintf(buffer, "global:\n");
129*4882a593Smuzhiyun snd_iprintf(buffer, " owner: %04x:%04x%08x\n",
130*4882a593Smuzhiyun buf.global.owner_hi >> 16,
131*4882a593Smuzhiyun buf.global.owner_hi & 0xffff, buf.global.owner_lo);
132*4882a593Smuzhiyun snd_iprintf(buffer, " notification: %08x\n", buf.global.notification);
133*4882a593Smuzhiyun dice_proc_fixup_string(buf.global.nick_name, NICK_NAME_SIZE);
134*4882a593Smuzhiyun snd_iprintf(buffer, " nick name: %s\n", buf.global.nick_name);
135*4882a593Smuzhiyun snd_iprintf(buffer, " clock select: %s %s\n",
136*4882a593Smuzhiyun str_from_array(clock_sources, ARRAY_SIZE(clock_sources),
137*4882a593Smuzhiyun buf.global.clock_select & CLOCK_SOURCE_MASK),
138*4882a593Smuzhiyun str_from_array(rates, ARRAY_SIZE(rates),
139*4882a593Smuzhiyun (buf.global.clock_select & CLOCK_RATE_MASK)
140*4882a593Smuzhiyun >> CLOCK_RATE_SHIFT));
141*4882a593Smuzhiyun snd_iprintf(buffer, " enable: %u\n", buf.global.enable);
142*4882a593Smuzhiyun snd_iprintf(buffer, " status: %slocked %s\n",
143*4882a593Smuzhiyun buf.global.status & STATUS_SOURCE_LOCKED ? "" : "un",
144*4882a593Smuzhiyun str_from_array(rates, ARRAY_SIZE(rates),
145*4882a593Smuzhiyun (buf.global.status &
146*4882a593Smuzhiyun STATUS_NOMINAL_RATE_MASK)
147*4882a593Smuzhiyun >> CLOCK_RATE_SHIFT));
148*4882a593Smuzhiyun snd_iprintf(buffer, " ext status: %08x\n", buf.global.extended_status);
149*4882a593Smuzhiyun snd_iprintf(buffer, " sample rate: %u\n", buf.global.sample_rate);
150*4882a593Smuzhiyun if (quadlets >= 90) {
151*4882a593Smuzhiyun snd_iprintf(buffer, " version: %u.%u.%u.%u\n",
152*4882a593Smuzhiyun (buf.global.version >> 24) & 0xff,
153*4882a593Smuzhiyun (buf.global.version >> 16) & 0xff,
154*4882a593Smuzhiyun (buf.global.version >> 8) & 0xff,
155*4882a593Smuzhiyun (buf.global.version >> 0) & 0xff);
156*4882a593Smuzhiyun snd_iprintf(buffer, " clock caps:");
157*4882a593Smuzhiyun for (i = 0; i <= 6; ++i)
158*4882a593Smuzhiyun if (buf.global.clock_caps & (1 << i))
159*4882a593Smuzhiyun snd_iprintf(buffer, " %s", rates[i]);
160*4882a593Smuzhiyun for (i = 0; i <= 12; ++i)
161*4882a593Smuzhiyun if (buf.global.clock_caps & (1 << (16 + i)))
162*4882a593Smuzhiyun snd_iprintf(buffer, " %s", clock_sources[i]);
163*4882a593Smuzhiyun snd_iprintf(buffer, "\n");
164*4882a593Smuzhiyun dice_proc_fixup_string(buf.global.clock_source_names,
165*4882a593Smuzhiyun CLOCK_SOURCE_NAMES_SIZE);
166*4882a593Smuzhiyun snd_iprintf(buffer, " clock source names: %s\n",
167*4882a593Smuzhiyun buf.global.clock_source_names);
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun if (dice_proc_read_mem(dice, &tx_rx_header, sections[2], 2) < 0)
171*4882a593Smuzhiyun return;
172*4882a593Smuzhiyun quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.tx) / 4);
173*4882a593Smuzhiyun for (stream = 0; stream < tx_rx_header.number; ++stream) {
174*4882a593Smuzhiyun if (dice_proc_read_mem(dice, &buf.tx, sections[2] + 2 +
175*4882a593Smuzhiyun stream * tx_rx_header.size,
176*4882a593Smuzhiyun quadlets) < 0)
177*4882a593Smuzhiyun break;
178*4882a593Smuzhiyun snd_iprintf(buffer, "tx %u:\n", stream);
179*4882a593Smuzhiyun snd_iprintf(buffer, " iso channel: %d\n", (int)buf.tx.iso);
180*4882a593Smuzhiyun snd_iprintf(buffer, " audio channels: %u\n",
181*4882a593Smuzhiyun buf.tx.number_audio);
182*4882a593Smuzhiyun snd_iprintf(buffer, " midi ports: %u\n", buf.tx.number_midi);
183*4882a593Smuzhiyun snd_iprintf(buffer, " speed: S%u\n", 100u << buf.tx.speed);
184*4882a593Smuzhiyun if (quadlets >= 68) {
185*4882a593Smuzhiyun dice_proc_fixup_string(buf.tx.names, TX_NAMES_SIZE);
186*4882a593Smuzhiyun snd_iprintf(buffer, " names: %s\n", buf.tx.names);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun if (quadlets >= 70) {
189*4882a593Smuzhiyun snd_iprintf(buffer, " ac3 caps: %08x\n",
190*4882a593Smuzhiyun buf.tx.ac3_caps);
191*4882a593Smuzhiyun snd_iprintf(buffer, " ac3 enable: %08x\n",
192*4882a593Smuzhiyun buf.tx.ac3_enable);
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun if (dice_proc_read_mem(dice, &tx_rx_header, sections[4], 2) < 0)
197*4882a593Smuzhiyun return;
198*4882a593Smuzhiyun quadlets = min_t(u32, tx_rx_header.size, sizeof(buf.rx) / 4);
199*4882a593Smuzhiyun for (stream = 0; stream < tx_rx_header.number; ++stream) {
200*4882a593Smuzhiyun if (dice_proc_read_mem(dice, &buf.rx, sections[4] + 2 +
201*4882a593Smuzhiyun stream * tx_rx_header.size,
202*4882a593Smuzhiyun quadlets) < 0)
203*4882a593Smuzhiyun break;
204*4882a593Smuzhiyun snd_iprintf(buffer, "rx %u:\n", stream);
205*4882a593Smuzhiyun snd_iprintf(buffer, " iso channel: %d\n", (int)buf.rx.iso);
206*4882a593Smuzhiyun snd_iprintf(buffer, " sequence start: %u\n", buf.rx.seq_start);
207*4882a593Smuzhiyun snd_iprintf(buffer, " audio channels: %u\n",
208*4882a593Smuzhiyun buf.rx.number_audio);
209*4882a593Smuzhiyun snd_iprintf(buffer, " midi ports: %u\n", buf.rx.number_midi);
210*4882a593Smuzhiyun if (quadlets >= 68) {
211*4882a593Smuzhiyun dice_proc_fixup_string(buf.rx.names, RX_NAMES_SIZE);
212*4882a593Smuzhiyun snd_iprintf(buffer, " names: %s\n", buf.rx.names);
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun if (quadlets >= 70) {
215*4882a593Smuzhiyun snd_iprintf(buffer, " ac3 caps: %08x\n",
216*4882a593Smuzhiyun buf.rx.ac3_caps);
217*4882a593Smuzhiyun snd_iprintf(buffer, " ac3 enable: %08x\n",
218*4882a593Smuzhiyun buf.rx.ac3_enable);
219*4882a593Smuzhiyun }
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun quadlets = min_t(u32, sections[7], sizeof(buf.ext_sync) / 4);
223*4882a593Smuzhiyun if (quadlets >= 4) {
224*4882a593Smuzhiyun if (dice_proc_read_mem(dice, &buf.ext_sync,
225*4882a593Smuzhiyun sections[6], 4) < 0)
226*4882a593Smuzhiyun return;
227*4882a593Smuzhiyun snd_iprintf(buffer, "ext status:\n");
228*4882a593Smuzhiyun snd_iprintf(buffer, " clock source: %s\n",
229*4882a593Smuzhiyun str_from_array(clock_sources,
230*4882a593Smuzhiyun ARRAY_SIZE(clock_sources),
231*4882a593Smuzhiyun buf.ext_sync.clock_source));
232*4882a593Smuzhiyun snd_iprintf(buffer, " locked: %u\n", buf.ext_sync.locked);
233*4882a593Smuzhiyun snd_iprintf(buffer, " rate: %s\n",
234*4882a593Smuzhiyun str_from_array(rates, ARRAY_SIZE(rates),
235*4882a593Smuzhiyun buf.ext_sync.rate));
236*4882a593Smuzhiyun snd_iprintf(buffer, " adat user data: ");
237*4882a593Smuzhiyun if (buf.ext_sync.adat_user_data & ADAT_USER_DATA_NO_DATA)
238*4882a593Smuzhiyun snd_iprintf(buffer, "-\n");
239*4882a593Smuzhiyun else
240*4882a593Smuzhiyun snd_iprintf(buffer, "%x\n",
241*4882a593Smuzhiyun buf.ext_sync.adat_user_data);
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun
dice_proc_read_formation(struct snd_info_entry * entry,struct snd_info_buffer * buffer)245*4882a593Smuzhiyun static void dice_proc_read_formation(struct snd_info_entry *entry,
246*4882a593Smuzhiyun struct snd_info_buffer *buffer)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun static const char *const rate_labels[] = {
249*4882a593Smuzhiyun [SND_DICE_RATE_MODE_LOW] = "low",
250*4882a593Smuzhiyun [SND_DICE_RATE_MODE_MIDDLE] = "middle",
251*4882a593Smuzhiyun [SND_DICE_RATE_MODE_HIGH] = "high",
252*4882a593Smuzhiyun };
253*4882a593Smuzhiyun struct snd_dice *dice = entry->private_data;
254*4882a593Smuzhiyun int i, j;
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun snd_iprintf(buffer, "Output stream from unit:\n");
257*4882a593Smuzhiyun for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
258*4882a593Smuzhiyun snd_iprintf(buffer, "\t%s", rate_labels[i]);
259*4882a593Smuzhiyun snd_iprintf(buffer, "\tMIDI\n");
260*4882a593Smuzhiyun for (i = 0; i < MAX_STREAMS; ++i) {
261*4882a593Smuzhiyun snd_iprintf(buffer, "Tx %u:", i);
262*4882a593Smuzhiyun for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j)
263*4882a593Smuzhiyun snd_iprintf(buffer, "\t%u", dice->tx_pcm_chs[i][j]);
264*4882a593Smuzhiyun snd_iprintf(buffer, "\t%u\n", dice->tx_midi_ports[i]);
265*4882a593Smuzhiyun }
266*4882a593Smuzhiyun
267*4882a593Smuzhiyun snd_iprintf(buffer, "Input stream to unit:\n");
268*4882a593Smuzhiyun for (i = 0; i < SND_DICE_RATE_MODE_COUNT; ++i)
269*4882a593Smuzhiyun snd_iprintf(buffer, "\t%s", rate_labels[i]);
270*4882a593Smuzhiyun snd_iprintf(buffer, "\n");
271*4882a593Smuzhiyun for (i = 0; i < MAX_STREAMS; ++i) {
272*4882a593Smuzhiyun snd_iprintf(buffer, "Rx %u:", i);
273*4882a593Smuzhiyun for (j = 0; j < SND_DICE_RATE_MODE_COUNT; ++j)
274*4882a593Smuzhiyun snd_iprintf(buffer, "\t%u", dice->rx_pcm_chs[i][j]);
275*4882a593Smuzhiyun snd_iprintf(buffer, "\t%u\n", dice->rx_midi_ports[i]);
276*4882a593Smuzhiyun }
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun
add_node(struct snd_dice * dice,struct snd_info_entry * root,const char * name,void (* op)(struct snd_info_entry * entry,struct snd_info_buffer * buffer))279*4882a593Smuzhiyun static void add_node(struct snd_dice *dice, struct snd_info_entry *root,
280*4882a593Smuzhiyun const char *name,
281*4882a593Smuzhiyun void (*op)(struct snd_info_entry *entry,
282*4882a593Smuzhiyun struct snd_info_buffer *buffer))
283*4882a593Smuzhiyun {
284*4882a593Smuzhiyun struct snd_info_entry *entry;
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun entry = snd_info_create_card_entry(dice->card, name, root);
287*4882a593Smuzhiyun if (entry)
288*4882a593Smuzhiyun snd_info_set_text_ops(entry, dice, op);
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun
snd_dice_create_proc(struct snd_dice * dice)291*4882a593Smuzhiyun void snd_dice_create_proc(struct snd_dice *dice)
292*4882a593Smuzhiyun {
293*4882a593Smuzhiyun struct snd_info_entry *root;
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun /*
296*4882a593Smuzhiyun * All nodes are automatically removed at snd_card_disconnect(),
297*4882a593Smuzhiyun * by following to link list.
298*4882a593Smuzhiyun */
299*4882a593Smuzhiyun root = snd_info_create_card_entry(dice->card, "firewire",
300*4882a593Smuzhiyun dice->card->proc_root);
301*4882a593Smuzhiyun if (!root)
302*4882a593Smuzhiyun return;
303*4882a593Smuzhiyun root->mode = S_IFDIR | 0555;
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun add_node(dice, root, "dice", dice_proc_read);
306*4882a593Smuzhiyun add_node(dice, root, "formation", dice_proc_read_formation);
307*4882a593Smuzhiyun }
308