162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * bebob_proc.c - a part of driver for BeBoB based devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2013-2014 Takashi Sakamoto
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "./bebob.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/* contents of information register */
1162306a36Sopenharmony_cistruct hw_info {
1262306a36Sopenharmony_ci	u64 manufacturer;
1362306a36Sopenharmony_ci	u32 protocol_ver;
1462306a36Sopenharmony_ci	u32 bld_ver;
1562306a36Sopenharmony_ci	u32 guid[2];
1662306a36Sopenharmony_ci	u32 model_id;
1762306a36Sopenharmony_ci	u32 model_rev;
1862306a36Sopenharmony_ci	u64 fw_date;
1962306a36Sopenharmony_ci	u64 fw_time;
2062306a36Sopenharmony_ci	u32 fw_id;
2162306a36Sopenharmony_ci	u32 fw_ver;
2262306a36Sopenharmony_ci	u32 base_addr;
2362306a36Sopenharmony_ci	u32 max_size;
2462306a36Sopenharmony_ci	u64 bld_date;
2562306a36Sopenharmony_ci	u64 bld_time;
2662306a36Sopenharmony_ci/* may not used in product
2762306a36Sopenharmony_ci	u64 dbg_date;
2862306a36Sopenharmony_ci	u64 dbg_time;
2962306a36Sopenharmony_ci	u32 dbg_id;
3062306a36Sopenharmony_ci	u32 dbg_version;
3162306a36Sopenharmony_ci*/
3262306a36Sopenharmony_ci} __packed;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic void
3562306a36Sopenharmony_ciproc_read_hw_info(struct snd_info_entry *entry,
3662306a36Sopenharmony_ci		  struct snd_info_buffer *buffer)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	struct snd_bebob *bebob = entry->private_data;
3962306a36Sopenharmony_ci	struct hw_info *info;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	info = kzalloc(sizeof(struct hw_info), GFP_KERNEL);
4262306a36Sopenharmony_ci	if (info == NULL)
4362306a36Sopenharmony_ci		return;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (snd_bebob_read_block(bebob->unit, 0,
4662306a36Sopenharmony_ci				   info, sizeof(struct hw_info)) < 0)
4762306a36Sopenharmony_ci		goto end;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	snd_iprintf(buffer, "Manufacturer:\t%.8s\n",
5062306a36Sopenharmony_ci		    (char *)&info->manufacturer);
5162306a36Sopenharmony_ci	snd_iprintf(buffer, "Protocol Ver:\t%d\n", info->protocol_ver);
5262306a36Sopenharmony_ci	snd_iprintf(buffer, "Build Ver:\t%d\n", info->bld_ver);
5362306a36Sopenharmony_ci	snd_iprintf(buffer, "GUID:\t\t0x%.8X%.8X\n",
5462306a36Sopenharmony_ci		    info->guid[0], info->guid[1]);
5562306a36Sopenharmony_ci	snd_iprintf(buffer, "Model ID:\t0x%02X\n", info->model_id);
5662306a36Sopenharmony_ci	snd_iprintf(buffer, "Model Rev:\t%d\n", info->model_rev);
5762306a36Sopenharmony_ci	snd_iprintf(buffer, "Firmware Date:\t%.8s\n", (char *)&info->fw_date);
5862306a36Sopenharmony_ci	snd_iprintf(buffer, "Firmware Time:\t%.8s\n", (char *)&info->fw_time);
5962306a36Sopenharmony_ci	snd_iprintf(buffer, "Firmware ID:\t0x%X\n", info->fw_id);
6062306a36Sopenharmony_ci	snd_iprintf(buffer, "Firmware Ver:\t%d\n", info->fw_ver);
6162306a36Sopenharmony_ci	snd_iprintf(buffer, "Base Addr:\t0x%X\n", info->base_addr);
6262306a36Sopenharmony_ci	snd_iprintf(buffer, "Max Size:\t%d\n", info->max_size);
6362306a36Sopenharmony_ci	snd_iprintf(buffer, "Loader Date:\t%.8s\n", (char *)&info->bld_date);
6462306a36Sopenharmony_ci	snd_iprintf(buffer, "Loader Time:\t%.8s\n", (char *)&info->bld_time);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciend:
6762306a36Sopenharmony_ci	kfree(info);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic void
7162306a36Sopenharmony_ciproc_read_meters(struct snd_info_entry *entry,
7262306a36Sopenharmony_ci		 struct snd_info_buffer *buffer)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	struct snd_bebob *bebob = entry->private_data;
7562306a36Sopenharmony_ci	const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
7662306a36Sopenharmony_ci	u32 *buf;
7762306a36Sopenharmony_ci	unsigned int i, c, channels, size;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	if (spec == NULL)
8062306a36Sopenharmony_ci		return;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	channels = spec->num * 2;
8362306a36Sopenharmony_ci	size = channels * sizeof(u32);
8462306a36Sopenharmony_ci	buf = kmalloc(size, GFP_KERNEL);
8562306a36Sopenharmony_ci	if (buf == NULL)
8662306a36Sopenharmony_ci		return;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (spec->get(bebob, buf, size) < 0)
8962306a36Sopenharmony_ci		goto end;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	for (i = 0, c = 1; i < channels; i++) {
9262306a36Sopenharmony_ci		snd_iprintf(buffer, "%s %d:\t%d\n",
9362306a36Sopenharmony_ci			    spec->labels[i / 2], c++, buf[i]);
9462306a36Sopenharmony_ci		if ((i + 1 < channels - 1) &&
9562306a36Sopenharmony_ci		    (strcmp(spec->labels[i / 2],
9662306a36Sopenharmony_ci			    spec->labels[(i + 1) / 2]) != 0))
9762306a36Sopenharmony_ci			c = 1;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ciend:
10062306a36Sopenharmony_ci	kfree(buf);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void
10462306a36Sopenharmony_ciproc_read_formation(struct snd_info_entry *entry,
10562306a36Sopenharmony_ci		struct snd_info_buffer *buffer)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct snd_bebob *bebob = entry->private_data;
10862306a36Sopenharmony_ci	struct snd_bebob_stream_formation *formation;
10962306a36Sopenharmony_ci	unsigned int i;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	snd_iprintf(buffer, "Output Stream from device:\n");
11262306a36Sopenharmony_ci	snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
11362306a36Sopenharmony_ci	formation = bebob->tx_stream_formations;
11462306a36Sopenharmony_ci	for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
11562306a36Sopenharmony_ci		snd_iprintf(buffer,
11662306a36Sopenharmony_ci			    "\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
11762306a36Sopenharmony_ci			    formation[i].pcm, formation[i].midi);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	snd_iprintf(buffer, "Input Stream to device:\n");
12162306a36Sopenharmony_ci	snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
12262306a36Sopenharmony_ci	formation = bebob->rx_stream_formations;
12362306a36Sopenharmony_ci	for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
12462306a36Sopenharmony_ci		snd_iprintf(buffer,
12562306a36Sopenharmony_ci			    "\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
12662306a36Sopenharmony_ci			    formation[i].pcm, formation[i].midi);
12762306a36Sopenharmony_ci	}
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void
13162306a36Sopenharmony_ciproc_read_clock(struct snd_info_entry *entry,
13262306a36Sopenharmony_ci		struct snd_info_buffer *buffer)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	static const char *const clk_labels[] = {
13562306a36Sopenharmony_ci		"Internal",
13662306a36Sopenharmony_ci		"External",
13762306a36Sopenharmony_ci		"SYT-Match",
13862306a36Sopenharmony_ci	};
13962306a36Sopenharmony_ci	struct snd_bebob *bebob = entry->private_data;
14062306a36Sopenharmony_ci	const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
14162306a36Sopenharmony_ci	const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
14262306a36Sopenharmony_ci	enum snd_bebob_clock_type src;
14362306a36Sopenharmony_ci	unsigned int rate;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	if (rate_spec->get(bebob, &rate) >= 0)
14662306a36Sopenharmony_ci		snd_iprintf(buffer, "Sampling rate: %d\n", rate);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (snd_bebob_stream_get_clock_src(bebob, &src) >= 0) {
14962306a36Sopenharmony_ci		if (clk_spec)
15062306a36Sopenharmony_ci			snd_iprintf(buffer, "Clock Source: %s\n",
15162306a36Sopenharmony_ci				    clk_labels[src]);
15262306a36Sopenharmony_ci		else
15362306a36Sopenharmony_ci			snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n",
15462306a36Sopenharmony_ci				    clk_labels[src], bebob->sync_input_plug);
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic void
15962306a36Sopenharmony_ciadd_node(struct snd_bebob *bebob, struct snd_info_entry *root, const char *name,
16062306a36Sopenharmony_ci	 void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct snd_info_entry *entry;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	entry = snd_info_create_card_entry(bebob->card, name, root);
16562306a36Sopenharmony_ci	if (entry)
16662306a36Sopenharmony_ci		snd_info_set_text_ops(entry, bebob, op);
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_civoid snd_bebob_proc_init(struct snd_bebob *bebob)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct snd_info_entry *root;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/*
17462306a36Sopenharmony_ci	 * All nodes are automatically removed at snd_card_disconnect(),
17562306a36Sopenharmony_ci	 * by following to link list.
17662306a36Sopenharmony_ci	 */
17762306a36Sopenharmony_ci	root = snd_info_create_card_entry(bebob->card, "firewire",
17862306a36Sopenharmony_ci					  bebob->card->proc_root);
17962306a36Sopenharmony_ci	if (root == NULL)
18062306a36Sopenharmony_ci		return;
18162306a36Sopenharmony_ci	root->mode = S_IFDIR | 0555;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	add_node(bebob, root, "clock", proc_read_clock);
18462306a36Sopenharmony_ci	add_node(bebob, root, "firmware", proc_read_hw_info);
18562306a36Sopenharmony_ci	add_node(bebob, root, "formation", proc_read_formation);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (bebob->spec->meter != NULL)
18862306a36Sopenharmony_ci		add_node(bebob, root, "meter", proc_read_meters);
18962306a36Sopenharmony_ci}
190