162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * fireworks_proc.c - a part of driver for Fireworks based devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2009-2010 Clemens Ladisch
662306a36Sopenharmony_ci * Copyright (c) 2013-2014 Takashi Sakamoto
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "./fireworks.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic inline const char*
1262306a36Sopenharmony_ciget_phys_name(struct snd_efw_phys_grp *grp, bool input)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	static const char *const ch_type[] = {
1562306a36Sopenharmony_ci		"Analog", "S/PDIF", "ADAT", "S/PDIF or ADAT", "Mirroring",
1662306a36Sopenharmony_ci		"Headphones", "I2S", "Guitar", "Pirzo Guitar", "Guitar String",
1762306a36Sopenharmony_ci	};
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	if (grp->type < ARRAY_SIZE(ch_type))
2062306a36Sopenharmony_ci		return ch_type[grp->type];
2162306a36Sopenharmony_ci	else if (input)
2262306a36Sopenharmony_ci		return "Input";
2362306a36Sopenharmony_ci	else
2462306a36Sopenharmony_ci		return "Output";
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic void
2862306a36Sopenharmony_ciproc_read_hwinfo(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct snd_efw *efw = entry->private_data;
3162306a36Sopenharmony_ci	unsigned short i;
3262306a36Sopenharmony_ci	struct snd_efw_hwinfo *hwinfo;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	hwinfo = kmalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
3562306a36Sopenharmony_ci	if (hwinfo == NULL)
3662306a36Sopenharmony_ci		return;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (snd_efw_command_get_hwinfo(efw, hwinfo) < 0)
3962306a36Sopenharmony_ci		goto end;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	snd_iprintf(buffer, "guid_hi: 0x%X\n", hwinfo->guid_hi);
4262306a36Sopenharmony_ci	snd_iprintf(buffer, "guid_lo: 0x%X\n", hwinfo->guid_lo);
4362306a36Sopenharmony_ci	snd_iprintf(buffer, "type: 0x%X\n", hwinfo->type);
4462306a36Sopenharmony_ci	snd_iprintf(buffer, "version: 0x%X\n", hwinfo->version);
4562306a36Sopenharmony_ci	snd_iprintf(buffer, "vendor_name: %s\n", hwinfo->vendor_name);
4662306a36Sopenharmony_ci	snd_iprintf(buffer, "model_name: %s\n", hwinfo->model_name);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	snd_iprintf(buffer, "dsp_version: 0x%X\n", hwinfo->dsp_version);
4962306a36Sopenharmony_ci	snd_iprintf(buffer, "arm_version: 0x%X\n", hwinfo->arm_version);
5062306a36Sopenharmony_ci	snd_iprintf(buffer, "fpga_version: 0x%X\n", hwinfo->fpga_version);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	snd_iprintf(buffer, "flags: 0x%X\n", hwinfo->flags);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	snd_iprintf(buffer, "max_sample_rate: 0x%X\n", hwinfo->max_sample_rate);
5562306a36Sopenharmony_ci	snd_iprintf(buffer, "min_sample_rate: 0x%X\n", hwinfo->min_sample_rate);
5662306a36Sopenharmony_ci	snd_iprintf(buffer, "supported_clock: 0x%X\n",
5762306a36Sopenharmony_ci		    hwinfo->supported_clocks);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	snd_iprintf(buffer, "phys out: 0x%X\n", hwinfo->phys_out);
6062306a36Sopenharmony_ci	snd_iprintf(buffer, "phys in: 0x%X\n", hwinfo->phys_in);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	snd_iprintf(buffer, "phys in grps: 0x%X\n",
6362306a36Sopenharmony_ci		    hwinfo->phys_in_grp_count);
6462306a36Sopenharmony_ci	for (i = 0; i < hwinfo->phys_in_grp_count; i++) {
6562306a36Sopenharmony_ci		snd_iprintf(buffer,
6662306a36Sopenharmony_ci			    "phys in grp[%d]: type 0x%X, count 0x%X\n",
6762306a36Sopenharmony_ci			    i, hwinfo->phys_out_grps[i].type,
6862306a36Sopenharmony_ci			    hwinfo->phys_out_grps[i].count);
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	snd_iprintf(buffer, "phys out grps: 0x%X\n",
7262306a36Sopenharmony_ci		    hwinfo->phys_out_grp_count);
7362306a36Sopenharmony_ci	for (i = 0; i < hwinfo->phys_out_grp_count; i++) {
7462306a36Sopenharmony_ci		snd_iprintf(buffer,
7562306a36Sopenharmony_ci			    "phys out grps[%d]: type 0x%X, count 0x%X\n",
7662306a36Sopenharmony_ci			    i, hwinfo->phys_out_grps[i].type,
7762306a36Sopenharmony_ci			    hwinfo->phys_out_grps[i].count);
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	snd_iprintf(buffer, "amdtp rx pcm channels 1x: 0x%X\n",
8162306a36Sopenharmony_ci		    hwinfo->amdtp_rx_pcm_channels);
8262306a36Sopenharmony_ci	snd_iprintf(buffer, "amdtp tx pcm channels 1x: 0x%X\n",
8362306a36Sopenharmony_ci		    hwinfo->amdtp_tx_pcm_channels);
8462306a36Sopenharmony_ci	snd_iprintf(buffer, "amdtp rx pcm channels 2x: 0x%X\n",
8562306a36Sopenharmony_ci		    hwinfo->amdtp_rx_pcm_channels_2x);
8662306a36Sopenharmony_ci	snd_iprintf(buffer, "amdtp tx pcm channels 2x: 0x%X\n",
8762306a36Sopenharmony_ci		    hwinfo->amdtp_tx_pcm_channels_2x);
8862306a36Sopenharmony_ci	snd_iprintf(buffer, "amdtp rx pcm channels 4x: 0x%X\n",
8962306a36Sopenharmony_ci		    hwinfo->amdtp_rx_pcm_channels_4x);
9062306a36Sopenharmony_ci	snd_iprintf(buffer, "amdtp tx pcm channels 4x: 0x%X\n",
9162306a36Sopenharmony_ci		    hwinfo->amdtp_tx_pcm_channels_4x);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	snd_iprintf(buffer, "midi out ports: 0x%X\n", hwinfo->midi_out_ports);
9462306a36Sopenharmony_ci	snd_iprintf(buffer, "midi in ports: 0x%X\n", hwinfo->midi_in_ports);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	snd_iprintf(buffer, "mixer playback channels: 0x%X\n",
9762306a36Sopenharmony_ci		    hwinfo->mixer_playback_channels);
9862306a36Sopenharmony_ci	snd_iprintf(buffer, "mixer capture channels: 0x%X\n",
9962306a36Sopenharmony_ci		    hwinfo->mixer_capture_channels);
10062306a36Sopenharmony_ciend:
10162306a36Sopenharmony_ci	kfree(hwinfo);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void
10562306a36Sopenharmony_ciproc_read_clock(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	struct snd_efw *efw = entry->private_data;
10862306a36Sopenharmony_ci	enum snd_efw_clock_source clock_source;
10962306a36Sopenharmony_ci	unsigned int sampling_rate;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (snd_efw_command_get_clock_source(efw, &clock_source) < 0)
11262306a36Sopenharmony_ci		return;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	if (snd_efw_command_get_sampling_rate(efw, &sampling_rate) < 0)
11562306a36Sopenharmony_ci		return;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	snd_iprintf(buffer, "Clock Source: %d\n", clock_source);
11862306a36Sopenharmony_ci	snd_iprintf(buffer, "Sampling Rate: %d\n", sampling_rate);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/*
12262306a36Sopenharmony_ci * NOTE:
12362306a36Sopenharmony_ci *  dB = 20 * log10(linear / 0x01000000)
12462306a36Sopenharmony_ci *  -144.0 dB when linear is 0
12562306a36Sopenharmony_ci */
12662306a36Sopenharmony_cistatic void
12762306a36Sopenharmony_ciproc_read_phys_meters(struct snd_info_entry *entry,
12862306a36Sopenharmony_ci		      struct snd_info_buffer *buffer)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct snd_efw *efw = entry->private_data;
13162306a36Sopenharmony_ci	struct snd_efw_phys_meters *meters;
13262306a36Sopenharmony_ci	unsigned int g, c, m, max, size;
13362306a36Sopenharmony_ci	const char *name;
13462306a36Sopenharmony_ci	u32 *linear;
13562306a36Sopenharmony_ci	int err;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	size = sizeof(struct snd_efw_phys_meters) +
13862306a36Sopenharmony_ci	       (efw->phys_in + efw->phys_out) * sizeof(u32);
13962306a36Sopenharmony_ci	meters = kzalloc(size, GFP_KERNEL);
14062306a36Sopenharmony_ci	if (meters == NULL)
14162306a36Sopenharmony_ci		return;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	err = snd_efw_command_get_phys_meters(efw, meters, size);
14462306a36Sopenharmony_ci	if (err < 0)
14562306a36Sopenharmony_ci		goto end;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	snd_iprintf(buffer, "Physical Meters:\n");
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	m = 0;
15062306a36Sopenharmony_ci	max = min(efw->phys_out, meters->out_meters);
15162306a36Sopenharmony_ci	linear = meters->values;
15262306a36Sopenharmony_ci	snd_iprintf(buffer, " %d Outputs:\n", max);
15362306a36Sopenharmony_ci	for (g = 0; g < efw->phys_out_grp_count; g++) {
15462306a36Sopenharmony_ci		name = get_phys_name(&efw->phys_out_grps[g], false);
15562306a36Sopenharmony_ci		for (c = 0; c < efw->phys_out_grps[g].count; c++) {
15662306a36Sopenharmony_ci			if (m < max)
15762306a36Sopenharmony_ci				snd_iprintf(buffer, "\t%s [%d]: %d\n",
15862306a36Sopenharmony_ci					    name, c, linear[m++]);
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	m = 0;
16362306a36Sopenharmony_ci	max = min(efw->phys_in, meters->in_meters);
16462306a36Sopenharmony_ci	linear = meters->values + meters->out_meters;
16562306a36Sopenharmony_ci	snd_iprintf(buffer, " %d Inputs:\n", max);
16662306a36Sopenharmony_ci	for (g = 0; g < efw->phys_in_grp_count; g++) {
16762306a36Sopenharmony_ci		name = get_phys_name(&efw->phys_in_grps[g], true);
16862306a36Sopenharmony_ci		for (c = 0; c < efw->phys_in_grps[g].count; c++)
16962306a36Sopenharmony_ci			if (m < max)
17062306a36Sopenharmony_ci				snd_iprintf(buffer, "\t%s [%d]: %d\n",
17162306a36Sopenharmony_ci					    name, c, linear[m++]);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ciend:
17462306a36Sopenharmony_ci	kfree(meters);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void
17862306a36Sopenharmony_ciproc_read_queues_state(struct snd_info_entry *entry,
17962306a36Sopenharmony_ci		       struct snd_info_buffer *buffer)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct snd_efw *efw = entry->private_data;
18262306a36Sopenharmony_ci	unsigned int consumed;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	if (efw->pull_ptr > efw->push_ptr)
18562306a36Sopenharmony_ci		consumed = snd_efw_resp_buf_size -
18662306a36Sopenharmony_ci			   (unsigned int)(efw->pull_ptr - efw->push_ptr);
18762306a36Sopenharmony_ci	else
18862306a36Sopenharmony_ci		consumed = (unsigned int)(efw->push_ptr - efw->pull_ptr);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	snd_iprintf(buffer, "%d/%d\n",
19162306a36Sopenharmony_ci		    consumed, snd_efw_resp_buf_size);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic void
19562306a36Sopenharmony_ciadd_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name,
19662306a36Sopenharmony_ci	 void (*op)(struct snd_info_entry *e, struct snd_info_buffer *b))
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct snd_info_entry *entry;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	entry = snd_info_create_card_entry(efw->card, name, root);
20162306a36Sopenharmony_ci	if (entry)
20262306a36Sopenharmony_ci		snd_info_set_text_ops(entry, efw, op);
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_civoid snd_efw_proc_init(struct snd_efw *efw)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct snd_info_entry *root;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/*
21062306a36Sopenharmony_ci	 * All nodes are automatically removed at snd_card_disconnect(),
21162306a36Sopenharmony_ci	 * by following to link list.
21262306a36Sopenharmony_ci	 */
21362306a36Sopenharmony_ci	root = snd_info_create_card_entry(efw->card, "firewire",
21462306a36Sopenharmony_ci					  efw->card->proc_root);
21562306a36Sopenharmony_ci	if (root == NULL)
21662306a36Sopenharmony_ci		return;
21762306a36Sopenharmony_ci	root->mode = S_IFDIR | 0555;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	add_node(efw, root, "clock", proc_read_clock);
22062306a36Sopenharmony_ci	add_node(efw, root, "firmware", proc_read_hwinfo);
22162306a36Sopenharmony_ci	add_node(efw, root, "meters", proc_read_phys_meters);
22262306a36Sopenharmony_ci	add_node(efw, root, "queues", proc_read_queues_state);
22362306a36Sopenharmony_ci}
224