// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * hdspe_proc.c
 * @brief RME HDSPe driver proc interface.
 *
 * 20210728 - Philippe.Bekaert@uhasselt.be
 *
 * Based on earlier work of the other MODULE_AUTHORs,
 * information kindly made available by RME (www.rme-audio.com),
 * and hardware kindly made available by Amptec Belgium (www.amptec.be).
 */

#include "hdspe.h"
#include "hdspe_core.h"


/*------------------------------------------------------------
   /proc interface
 ------------------------------------------------------------*/

static void hdspe_iprintf_reg_bits(struct snd_info_buffer *buffer,
				   __le32 reg,
				   const char* const *bitNames)
{
	uint32_t r = le32_to_cpu(reg);
	int i, k;
	for (i=0, k=0; i<32; i++) {
		if (r & (1<<i)) {
			snd_iprintf(buffer, "%s%s", k>0 ? "|" : "",
				    bitNames[i]);
			k++;
		}
	}
}

void hdspe_iprintf_reg(struct snd_info_buffer *buffer,
		       const char* name, __le32 reg,
		       const char* const *bitNames)
{
	snd_iprintf(buffer, "%-8s: 0x%08x \t", name, le32_to_cpu(reg));
	if (bitNames)
		hdspe_iprintf_reg_bits(buffer, reg, bitNames);
	snd_iprintf(buffer, "\n");
}

void hdspe_iprint_fbits(struct snd_info_buffer *buffer,
			const char* name, u32 fbits)
{
	int i;
	snd_iprintf(buffer, "%s\t: 0x%08x \t", name, fbits);
	for (i=0; i<8; i++)
		snd_iprintf(buffer, "%d ", HDSPE_FBITS_FREQ(fbits, i));
	snd_iprintf(buffer, "\n");
}

void hdspe_proc_read_common(struct snd_info_buffer *buffer,
			    struct hdspe* hdspe,
			    struct hdspe_status* s)
{
	int i;
	hdspe->m.read_status(hdspe, s);

	snd_iprintf(buffer, "RME HDSPe %s serial %08d firmware %d\n\n",
		    HDSPE_IO_TYPE_NAME(hdspe->io_type),
		    hdspe->serial, hdspe->fw_build);

	snd_iprintf(buffer, "Buffer size\t\t: %u\n",
		    s->buffer_size);
	snd_iprintf(buffer, "System sample rate\t: %lu\n",
		    (long unsigned)div_u64(s->sample_rate_numerator,
					   (u64)s->sample_rate_denominator));
	snd_iprintf(buffer, "Internal sample rate\t: %lu\n",
		    (long unsigned)div_u64(s->sample_rate_numerator,
				   (u64)s->internal_sample_rate_denominator));
	snd_iprintf(buffer, "Speed mode\t\t: %d %s\n", s->speed_mode,
		    HDSPE_SPEED_NAME(s->speed_mode));
	snd_iprintf(buffer, "Clock mode\t\t: %d %s\n", s->clock_mode,
		    HDSPE_CLOCK_MODE_NAME(s->clock_mode));
	snd_iprintf(buffer, "Preferred clock source\t: %d %s\n", s->preferred_ref,
		    hdspe_clock_source_name(hdspe, s->preferred_ref));
	snd_iprintf(buffer, "AutoSync ref\t\t: %d %s\n", s->autosync_ref,
		    hdspe_clock_source_name(hdspe, s->autosync_ref));
	snd_iprintf(buffer, "Internal freq\t\t: %d %s\n", s->internal_freq,
		    hdspe_freq_name(s->internal_freq));
	snd_iprintf(buffer, "External freq\t\t: %d %s\n", s->external_freq,
		    hdspe_freq_name(s->external_freq));
	for (i=0; i<HDSPE_CLOCK_SOURCE_COUNT; i++) {
		if (s->sync[i] != HDSPE_SYNC_STATUS_NOT_AVAILABLE)
			snd_iprintf(buffer, "Sync source %2d %-8s\t: freq %d %-9s, sync %d %-9s %s\n",
				    i,
				    hdspe_clock_source_name(hdspe, i),
				    s->freq[i],
				    hdspe_freq_name(s->freq[i]),
				    s->sync[i],
				    HDSPE_SYNC_STATUS_NAME(s->sync[i]),
				    i == s->autosync_ref ? "*" : "");
	}
	snd_iprintf(buffer, "Single speed WCLK output: %d %s\n", s->wck48,
		    HDSPE_BOOL_NAME(s->wck48));
	snd_iprintf(buffer, "Clear TMS               : %d %s\n", s->clr_tms,
		    HDSPE_BOOL_NAME(s->clr_tms));
	snd_iprintf(buffer, "\n");
}

void hdspe_proc_read_common2(struct snd_info_buffer *buffer,
			     struct hdspe* hdspe,
			     struct hdspe_status* s)
{
	int i;
	
	union hdspe_status0_reg status0 = hdspe_read_status0(hdspe);
	snd_iprintf(buffer, "\n");
	snd_iprintf(buffer, "BUF_PTR\t: %05d\nBUF_ID\t: %d\nID_PTR\t: %05d\n",
		    le16_to_cpu(status0.common.BUF_PTR)<<6,
		    status0.common.BUF_ID,
		    status0.common.BUF_ID * s->buffer_size*4);
	snd_iprintf(buffer, "LAT\t: %d\n", hdspe->reg.control.common.LAT);
	
	snd_iprintf(buffer, "\n");
	snd_iprintf(buffer, "Running     \t: %d\n", hdspe->running);
	snd_iprintf(buffer, "Capture PID \t: %d\n", hdspe->capture_pid);
	snd_iprintf(buffer, "Playback PID\t: %d\n", hdspe->playback_pid);
	
	snd_iprintf(buffer, "\n");
	snd_iprintf(buffer, "Capture channel mapping:\n");
	for (i = 0 ; i < hdspe->max_channels_in; i ++) {
		snd_iprintf(buffer, "Logical %d DMA %d '%s'\n",
			    i, hdspe->channel_map_in[i], hdspe->port_names_in[i]);
	}
	snd_iprintf(buffer, "\nPlayback channel mapping:\n");
	for (i = 0 ; i < hdspe->max_channels_out; i ++) {
		snd_iprintf(buffer, "Logical %d DMA %d '%s'\n",
			    i, hdspe->channel_map_out[i], hdspe->port_names_out[i]);
	}
}

static void snd_hdspe_proc_ports_in(struct snd_info_entry *entry,
			  struct snd_info_buffer *buffer)
{
	struct hdspe *hdspe = entry->private_data;
	int i;

	snd_iprintf(buffer, "# generated by hdspe\n");

	for (i = 0; i < hdspe->max_channels_in; i++) {
		snd_iprintf(buffer, "%d=%s\n", i+1, hdspe->port_names_in[i]);
	}
}

static void snd_hdspe_proc_ports_out(struct snd_info_entry *entry,
			  struct snd_info_buffer *buffer)
{
	struct hdspe *hdspe = entry->private_data;
	int i;

	snd_iprintf(buffer, "# generated by hdspe\n");

	for (i = 0; i < hdspe->max_channels_out; i++) {
		snd_iprintf(buffer, "%d=%s\n", i+1, hdspe->port_names_out[i]);
	}
}

#ifdef CONFIG_SND_DEBUG
static void
snd_hdspe_proc_read_debug(struct snd_info_entry *entry,
			  struct snd_info_buffer *buffer)
{
	struct hdspe *hdspe = entry->private_data;
	int j,i;

	for (i = 0; i < 1024 /* 1024*64 */; i += j) {
		snd_iprintf(buffer, "%04d: ", i);
		for (j = 0; j < 16; j += 4)
			snd_iprintf(buffer, "%08X ", hdspe_read(hdspe, i + j));
		snd_iprintf(buffer, "\n");
	}
}
#endif

void snd_hdspe_proc_init(struct hdspe *hdspe)
{
	snd_card_ro_proc_new(hdspe->card, "hdspe", hdspe, hdspe->m.read_proc);
	snd_card_ro_proc_new(hdspe->card, "ports.in", hdspe,
			     snd_hdspe_proc_ports_in);
	snd_card_ro_proc_new(hdspe->card, "ports.out", hdspe,
			     snd_hdspe_proc_ports_out);
	if (hdspe->tco)
		snd_card_ro_proc_new(hdspe->card, "tco", hdspe,
				     snd_hdspe_proc_read_tco);
	snd_card_ro_proc_new(hdspe->card, "mixer", hdspe, hdspe_mixer_read_proc);

#ifdef CONFIG_SND_DEBUG
	/* debug file to read all hdspe registers */
	snd_card_ro_proc_new(hdspe->card, "debug", hdspe,
			     snd_hdspe_proc_read_debug);
#endif
}
