1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * bebob_proc.c - a part of driver for BeBoB based devices
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2013-2014 Takashi Sakamoto
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #include "./bebob.h"
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun /* contents of information register */
11*4882a593Smuzhiyun struct hw_info {
12*4882a593Smuzhiyun u64 manufacturer;
13*4882a593Smuzhiyun u32 protocol_ver;
14*4882a593Smuzhiyun u32 bld_ver;
15*4882a593Smuzhiyun u32 guid[2];
16*4882a593Smuzhiyun u32 model_id;
17*4882a593Smuzhiyun u32 model_rev;
18*4882a593Smuzhiyun u64 fw_date;
19*4882a593Smuzhiyun u64 fw_time;
20*4882a593Smuzhiyun u32 fw_id;
21*4882a593Smuzhiyun u32 fw_ver;
22*4882a593Smuzhiyun u32 base_addr;
23*4882a593Smuzhiyun u32 max_size;
24*4882a593Smuzhiyun u64 bld_date;
25*4882a593Smuzhiyun u64 bld_time;
26*4882a593Smuzhiyun /* may not used in product
27*4882a593Smuzhiyun u64 dbg_date;
28*4882a593Smuzhiyun u64 dbg_time;
29*4882a593Smuzhiyun u32 dbg_id;
30*4882a593Smuzhiyun u32 dbg_version;
31*4882a593Smuzhiyun */
32*4882a593Smuzhiyun } __packed;
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun static void
proc_read_hw_info(struct snd_info_entry * entry,struct snd_info_buffer * buffer)35*4882a593Smuzhiyun proc_read_hw_info(struct snd_info_entry *entry,
36*4882a593Smuzhiyun struct snd_info_buffer *buffer)
37*4882a593Smuzhiyun {
38*4882a593Smuzhiyun struct snd_bebob *bebob = entry->private_data;
39*4882a593Smuzhiyun struct hw_info *info;
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun info = kzalloc(sizeof(struct hw_info), GFP_KERNEL);
42*4882a593Smuzhiyun if (info == NULL)
43*4882a593Smuzhiyun return;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun if (snd_bebob_read_block(bebob->unit, 0,
46*4882a593Smuzhiyun info, sizeof(struct hw_info)) < 0)
47*4882a593Smuzhiyun goto end;
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun snd_iprintf(buffer, "Manufacturer:\t%.8s\n",
50*4882a593Smuzhiyun (char *)&info->manufacturer);
51*4882a593Smuzhiyun snd_iprintf(buffer, "Protocol Ver:\t%d\n", info->protocol_ver);
52*4882a593Smuzhiyun snd_iprintf(buffer, "Build Ver:\t%d\n", info->bld_ver);
53*4882a593Smuzhiyun snd_iprintf(buffer, "GUID:\t\t0x%.8X%.8X\n",
54*4882a593Smuzhiyun info->guid[0], info->guid[1]);
55*4882a593Smuzhiyun snd_iprintf(buffer, "Model ID:\t0x%02X\n", info->model_id);
56*4882a593Smuzhiyun snd_iprintf(buffer, "Model Rev:\t%d\n", info->model_rev);
57*4882a593Smuzhiyun snd_iprintf(buffer, "Firmware Date:\t%.8s\n", (char *)&info->fw_date);
58*4882a593Smuzhiyun snd_iprintf(buffer, "Firmware Time:\t%.8s\n", (char *)&info->fw_time);
59*4882a593Smuzhiyun snd_iprintf(buffer, "Firmware ID:\t0x%X\n", info->fw_id);
60*4882a593Smuzhiyun snd_iprintf(buffer, "Firmware Ver:\t%d\n", info->fw_ver);
61*4882a593Smuzhiyun snd_iprintf(buffer, "Base Addr:\t0x%X\n", info->base_addr);
62*4882a593Smuzhiyun snd_iprintf(buffer, "Max Size:\t%d\n", info->max_size);
63*4882a593Smuzhiyun snd_iprintf(buffer, "Loader Date:\t%.8s\n", (char *)&info->bld_date);
64*4882a593Smuzhiyun snd_iprintf(buffer, "Loader Time:\t%.8s\n", (char *)&info->bld_time);
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun end:
67*4882a593Smuzhiyun kfree(info);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun static void
proc_read_meters(struct snd_info_entry * entry,struct snd_info_buffer * buffer)71*4882a593Smuzhiyun proc_read_meters(struct snd_info_entry *entry,
72*4882a593Smuzhiyun struct snd_info_buffer *buffer)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun struct snd_bebob *bebob = entry->private_data;
75*4882a593Smuzhiyun const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
76*4882a593Smuzhiyun u32 *buf;
77*4882a593Smuzhiyun unsigned int i, c, channels, size;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun if (spec == NULL)
80*4882a593Smuzhiyun return;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun channels = spec->num * 2;
83*4882a593Smuzhiyun size = channels * sizeof(u32);
84*4882a593Smuzhiyun buf = kmalloc(size, GFP_KERNEL);
85*4882a593Smuzhiyun if (buf == NULL)
86*4882a593Smuzhiyun return;
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun if (spec->get(bebob, buf, size) < 0)
89*4882a593Smuzhiyun goto end;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun for (i = 0, c = 1; i < channels; i++) {
92*4882a593Smuzhiyun snd_iprintf(buffer, "%s %d:\t%d\n",
93*4882a593Smuzhiyun spec->labels[i / 2], c++, buf[i]);
94*4882a593Smuzhiyun if ((i + 1 < channels - 1) &&
95*4882a593Smuzhiyun (strcmp(spec->labels[i / 2],
96*4882a593Smuzhiyun spec->labels[(i + 1) / 2]) != 0))
97*4882a593Smuzhiyun c = 1;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun end:
100*4882a593Smuzhiyun kfree(buf);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun static void
proc_read_formation(struct snd_info_entry * entry,struct snd_info_buffer * buffer)104*4882a593Smuzhiyun proc_read_formation(struct snd_info_entry *entry,
105*4882a593Smuzhiyun struct snd_info_buffer *buffer)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun struct snd_bebob *bebob = entry->private_data;
108*4882a593Smuzhiyun struct snd_bebob_stream_formation *formation;
109*4882a593Smuzhiyun unsigned int i;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun snd_iprintf(buffer, "Output Stream from device:\n");
112*4882a593Smuzhiyun snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
113*4882a593Smuzhiyun formation = bebob->tx_stream_formations;
114*4882a593Smuzhiyun for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
115*4882a593Smuzhiyun snd_iprintf(buffer,
116*4882a593Smuzhiyun "\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
117*4882a593Smuzhiyun formation[i].pcm, formation[i].midi);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun snd_iprintf(buffer, "Input Stream to device:\n");
121*4882a593Smuzhiyun snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
122*4882a593Smuzhiyun formation = bebob->rx_stream_formations;
123*4882a593Smuzhiyun for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
124*4882a593Smuzhiyun snd_iprintf(buffer,
125*4882a593Smuzhiyun "\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
126*4882a593Smuzhiyun formation[i].pcm, formation[i].midi);
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun static void
proc_read_clock(struct snd_info_entry * entry,struct snd_info_buffer * buffer)131*4882a593Smuzhiyun proc_read_clock(struct snd_info_entry *entry,
132*4882a593Smuzhiyun struct snd_info_buffer *buffer)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun static const char *const clk_labels[] = {
135*4882a593Smuzhiyun "Internal",
136*4882a593Smuzhiyun "External",
137*4882a593Smuzhiyun "SYT-Match",
138*4882a593Smuzhiyun };
139*4882a593Smuzhiyun struct snd_bebob *bebob = entry->private_data;
140*4882a593Smuzhiyun const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
141*4882a593Smuzhiyun const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
142*4882a593Smuzhiyun enum snd_bebob_clock_type src;
143*4882a593Smuzhiyun unsigned int rate;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun if (rate_spec->get(bebob, &rate) >= 0)
146*4882a593Smuzhiyun snd_iprintf(buffer, "Sampling rate: %d\n", rate);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (snd_bebob_stream_get_clock_src(bebob, &src) >= 0) {
149*4882a593Smuzhiyun if (clk_spec)
150*4882a593Smuzhiyun snd_iprintf(buffer, "Clock Source: %s\n",
151*4882a593Smuzhiyun clk_labels[src]);
152*4882a593Smuzhiyun else
153*4882a593Smuzhiyun snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n",
154*4882a593Smuzhiyun clk_labels[src], bebob->sync_input_plug);
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun static void
add_node(struct snd_bebob * bebob,struct snd_info_entry * root,const char * name,void (* op)(struct snd_info_entry * e,struct snd_info_buffer * b))159*4882a593Smuzhiyun add_node(struct snd_bebob *bebob, struct snd_info_entry *root, const char *name,
160*4882a593Smuzhiyun void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun struct snd_info_entry *entry;
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun entry = snd_info_create_card_entry(bebob->card, name, root);
165*4882a593Smuzhiyun if (entry)
166*4882a593Smuzhiyun snd_info_set_text_ops(entry, bebob, op);
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
snd_bebob_proc_init(struct snd_bebob * bebob)169*4882a593Smuzhiyun void snd_bebob_proc_init(struct snd_bebob *bebob)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun struct snd_info_entry *root;
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun /*
174*4882a593Smuzhiyun * All nodes are automatically removed at snd_card_disconnect(),
175*4882a593Smuzhiyun * by following to link list.
176*4882a593Smuzhiyun */
177*4882a593Smuzhiyun root = snd_info_create_card_entry(bebob->card, "firewire",
178*4882a593Smuzhiyun bebob->card->proc_root);
179*4882a593Smuzhiyun if (root == NULL)
180*4882a593Smuzhiyun return;
181*4882a593Smuzhiyun root->mode = S_IFDIR | 0555;
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun add_node(bebob, root, "clock", proc_read_clock);
184*4882a593Smuzhiyun add_node(bebob, root, "firmware", proc_read_hw_info);
185*4882a593Smuzhiyun add_node(bebob, root, "formation", proc_read_formation);
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun if (bebob->spec->meter != NULL)
188*4882a593Smuzhiyun add_node(bebob, root, "meter", proc_read_meters);
189*4882a593Smuzhiyun }
190