18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * bebob_maudio.c - a part of driver for BeBoB based devices
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014 Takashi Sakamoto
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "./bebob.h"
98c2ecf20Sopenharmony_ci#include <sound/control.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci * Just powering on, Firewire 410/Audiophile/1814 and ProjectMix I/O wait to
138c2ecf20Sopenharmony_ci * download firmware blob. To enable these devices, drivers should upload
148c2ecf20Sopenharmony_ci * firmware blob and send a command to initialize configuration to factory
158c2ecf20Sopenharmony_ci * settings when completing uploading. Then these devices generate bus reset
168c2ecf20Sopenharmony_ci * and are recognized as new devices with the firmware.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci * But with firmware version 5058 or later, the firmware is stored to flash
198c2ecf20Sopenharmony_ci * memory in the device and drivers can tell bootloader to load the firmware
208c2ecf20Sopenharmony_ci * by sending a cue. This cue must be sent one time.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * For streaming, both of output and input streams are needed for Firewire 410
238c2ecf20Sopenharmony_ci * and Ozonic. The single stream is OK for the other devices even if the clock
248c2ecf20Sopenharmony_ci * source is not SYT-Match (I note no devices use SYT-Match).
258c2ecf20Sopenharmony_ci *
268c2ecf20Sopenharmony_ci * Without streaming, the devices except for Firewire Audiophile can mix any
278c2ecf20Sopenharmony_ci * input and output. For this reason, Audiophile cannot be used as standalone
288c2ecf20Sopenharmony_ci * mixer.
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * Firewire 1814 and ProjectMix I/O uses special firmware. It will be freezed
318c2ecf20Sopenharmony_ci * when receiving any commands which the firmware can't understand. These
328c2ecf20Sopenharmony_ci * devices utilize completely different system to control. It is some
338c2ecf20Sopenharmony_ci * write-transaction directly into a certain address. All of addresses for mixer
348c2ecf20Sopenharmony_ci * functionality is between 0xffc700700000 to 0xffc70070009c.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/* Offset from information register */
388c2ecf20Sopenharmony_ci#define INFO_OFFSET_SW_DATE	0x20
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* Bootloader Protocol Version 1 */
418c2ecf20Sopenharmony_ci#define MAUDIO_BOOTLOADER_CUE1	0x00000001
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * Initializing configuration to factory settings (= 0x1101), (swapped in line),
448c2ecf20Sopenharmony_ci * Command code is zero (= 0x00),
458c2ecf20Sopenharmony_ci * the number of operands is zero (= 0x00)(at least significant byte)
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_ci#define MAUDIO_BOOTLOADER_CUE2	0x01110000
488c2ecf20Sopenharmony_ci/* padding */
498c2ecf20Sopenharmony_ci#define MAUDIO_BOOTLOADER_CUE3	0x00000000
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci#define MAUDIO_SPECIFIC_ADDRESS	0xffc700000000ULL
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define METER_OFFSET		0x00600000
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* some device has sync info after metering data */
568c2ecf20Sopenharmony_ci#define METER_SIZE_SPECIAL	84	/* with sync info */
578c2ecf20Sopenharmony_ci#define METER_SIZE_FW410	76	/* with sync info */
588c2ecf20Sopenharmony_ci#define METER_SIZE_AUDIOPHILE	60	/* with sync info */
598c2ecf20Sopenharmony_ci#define METER_SIZE_SOLO		52	/* with sync info */
608c2ecf20Sopenharmony_ci#define METER_SIZE_OZONIC	48
618c2ecf20Sopenharmony_ci#define METER_SIZE_NRV10	80
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* labels for metering */
648c2ecf20Sopenharmony_ci#define ANA_IN		"Analog In"
658c2ecf20Sopenharmony_ci#define ANA_OUT		"Analog Out"
668c2ecf20Sopenharmony_ci#define DIG_IN		"Digital In"
678c2ecf20Sopenharmony_ci#define SPDIF_IN	"S/PDIF In"
688c2ecf20Sopenharmony_ci#define ADAT_IN		"ADAT In"
698c2ecf20Sopenharmony_ci#define DIG_OUT		"Digital Out"
708c2ecf20Sopenharmony_ci#define SPDIF_OUT	"S/PDIF Out"
718c2ecf20Sopenharmony_ci#define ADAT_OUT	"ADAT Out"
728c2ecf20Sopenharmony_ci#define STRM_IN		"Stream In"
738c2ecf20Sopenharmony_ci#define AUX_OUT		"Aux Out"
748c2ecf20Sopenharmony_ci#define HP_OUT		"HP Out"
758c2ecf20Sopenharmony_ci/* for NRV */
768c2ecf20Sopenharmony_ci#define UNKNOWN_METER	"Unknown"
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistruct special_params {
798c2ecf20Sopenharmony_ci	bool is1814;
808c2ecf20Sopenharmony_ci	unsigned int clk_src;
818c2ecf20Sopenharmony_ci	unsigned int dig_in_fmt;
828c2ecf20Sopenharmony_ci	unsigned int dig_out_fmt;
838c2ecf20Sopenharmony_ci	unsigned int clk_lock;
848c2ecf20Sopenharmony_ci	struct snd_ctl_elem_id *ctl_id_sync;
858c2ecf20Sopenharmony_ci};
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/*
888c2ecf20Sopenharmony_ci * For some M-Audio devices, this module just send cue to load firmware. After
898c2ecf20Sopenharmony_ci * loading, the device generates bus reset and newly detected.
908c2ecf20Sopenharmony_ci *
918c2ecf20Sopenharmony_ci * If we make any transactions to load firmware, the operation may failed.
928c2ecf20Sopenharmony_ci */
938c2ecf20Sopenharmony_ciint snd_bebob_maudio_load_firmware(struct fw_unit *unit)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	struct fw_device *device = fw_parent_device(unit);
968c2ecf20Sopenharmony_ci	int err, rcode;
978c2ecf20Sopenharmony_ci	u64 date;
988c2ecf20Sopenharmony_ci	__le32 *cues;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* check date of software used to build */
1018c2ecf20Sopenharmony_ci	err = snd_bebob_read_block(unit, INFO_OFFSET_SW_DATE,
1028c2ecf20Sopenharmony_ci				   &date, sizeof(u64));
1038c2ecf20Sopenharmony_ci	if (err < 0)
1048c2ecf20Sopenharmony_ci		return err;
1058c2ecf20Sopenharmony_ci	/*
1068c2ecf20Sopenharmony_ci	 * firmware version 5058 or later has date later than "20070401", but
1078c2ecf20Sopenharmony_ci	 * 'date' is not null-terminated.
1088c2ecf20Sopenharmony_ci	 */
1098c2ecf20Sopenharmony_ci	if (date < 0x3230303730343031LL) {
1108c2ecf20Sopenharmony_ci		dev_err(&unit->device,
1118c2ecf20Sopenharmony_ci			"Use firmware version 5058 or later\n");
1128c2ecf20Sopenharmony_ci		return -ENXIO;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	cues = kmalloc_array(3, sizeof(*cues), GFP_KERNEL);
1168c2ecf20Sopenharmony_ci	if (!cues)
1178c2ecf20Sopenharmony_ci		return -ENOMEM;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	cues[0] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE1);
1208c2ecf20Sopenharmony_ci	cues[1] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE2);
1218c2ecf20Sopenharmony_ci	cues[2] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE3);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	rcode = fw_run_transaction(device->card, TCODE_WRITE_BLOCK_REQUEST,
1248c2ecf20Sopenharmony_ci				   device->node_id, device->generation,
1258c2ecf20Sopenharmony_ci				   device->max_speed, BEBOB_ADDR_REG_REQ,
1268c2ecf20Sopenharmony_ci				   cues, 3 * sizeof(*cues));
1278c2ecf20Sopenharmony_ci	kfree(cues);
1288c2ecf20Sopenharmony_ci	if (rcode != RCODE_COMPLETE) {
1298c2ecf20Sopenharmony_ci		dev_err(&unit->device,
1308c2ecf20Sopenharmony_ci			"Failed to send a cue to load firmware\n");
1318c2ecf20Sopenharmony_ci		err = -EIO;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	return err;
1358c2ecf20Sopenharmony_ci}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic inline int
1388c2ecf20Sopenharmony_ciget_meter(struct snd_bebob *bebob, void *buf, unsigned int size)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	return snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
1418c2ecf20Sopenharmony_ci				  MAUDIO_SPECIFIC_ADDRESS + METER_OFFSET,
1428c2ecf20Sopenharmony_ci				  buf, size, 0);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int
1468c2ecf20Sopenharmony_cicheck_clk_sync(struct snd_bebob *bebob, unsigned int size, bool *sync)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	int err;
1498c2ecf20Sopenharmony_ci	u8 *buf;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	buf = kmalloc(size, GFP_KERNEL);
1528c2ecf20Sopenharmony_ci	if (buf == NULL)
1538c2ecf20Sopenharmony_ci		return -ENOMEM;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	err = get_meter(bebob, buf, size);
1568c2ecf20Sopenharmony_ci	if (err < 0)
1578c2ecf20Sopenharmony_ci		goto end;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* if synced, this value is the same as SFC of FDF in CIP header */
1608c2ecf20Sopenharmony_ci	*sync = (buf[size - 2] != 0xff);
1618c2ecf20Sopenharmony_ciend:
1628c2ecf20Sopenharmony_ci	kfree(buf);
1638c2ecf20Sopenharmony_ci	return err;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/*
1678c2ecf20Sopenharmony_ci * dig_fmt: 0x00:S/PDIF, 0x01:ADAT
1688c2ecf20Sopenharmony_ci * clk_lock: 0x00:unlock, 0x01:lock
1698c2ecf20Sopenharmony_ci */
1708c2ecf20Sopenharmony_cistatic int
1718c2ecf20Sopenharmony_ciavc_maudio_set_special_clk(struct snd_bebob *bebob, unsigned int clk_src,
1728c2ecf20Sopenharmony_ci			   unsigned int dig_in_fmt, unsigned int dig_out_fmt,
1738c2ecf20Sopenharmony_ci			   unsigned int clk_lock)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct special_params *params = bebob->maudio_special_quirk;
1768c2ecf20Sopenharmony_ci	int err;
1778c2ecf20Sopenharmony_ci	u8 *buf;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (amdtp_stream_running(&bebob->rx_stream) ||
1808c2ecf20Sopenharmony_ci	    amdtp_stream_running(&bebob->tx_stream))
1818c2ecf20Sopenharmony_ci		return -EBUSY;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	buf = kmalloc(12, GFP_KERNEL);
1848c2ecf20Sopenharmony_ci	if (buf == NULL)
1858c2ecf20Sopenharmony_ci		return -ENOMEM;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	buf[0]  = 0x00;		/* CONTROL */
1888c2ecf20Sopenharmony_ci	buf[1]  = 0xff;		/* UNIT */
1898c2ecf20Sopenharmony_ci	buf[2]  = 0x00;		/* vendor dependent */
1908c2ecf20Sopenharmony_ci	buf[3]  = 0x04;		/* company ID high */
1918c2ecf20Sopenharmony_ci	buf[4]  = 0x00;		/* company ID middle */
1928c2ecf20Sopenharmony_ci	buf[5]  = 0x04;		/* company ID low */
1938c2ecf20Sopenharmony_ci	buf[6]  = 0xff & clk_src;	/* clock source */
1948c2ecf20Sopenharmony_ci	buf[7]  = 0xff & dig_in_fmt;	/* input digital format */
1958c2ecf20Sopenharmony_ci	buf[8]  = 0xff & dig_out_fmt;	/* output digital format */
1968c2ecf20Sopenharmony_ci	buf[9]  = 0xff & clk_lock;	/* lock these settings */
1978c2ecf20Sopenharmony_ci	buf[10] = 0x00;		/* padding  */
1988c2ecf20Sopenharmony_ci	buf[11] = 0x00;		/* padding */
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	err = fcp_avc_transaction(bebob->unit, buf, 12, buf, 12,
2018c2ecf20Sopenharmony_ci				  BIT(1) | BIT(2) | BIT(3) | BIT(4) |
2028c2ecf20Sopenharmony_ci				  BIT(5) | BIT(6) | BIT(7) | BIT(8) |
2038c2ecf20Sopenharmony_ci				  BIT(9));
2048c2ecf20Sopenharmony_ci	if ((err > 0) && (err < 10))
2058c2ecf20Sopenharmony_ci		err = -EIO;
2068c2ecf20Sopenharmony_ci	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
2078c2ecf20Sopenharmony_ci		err = -ENOSYS;
2088c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0a) /* REJECTED */
2098c2ecf20Sopenharmony_ci		err = -EINVAL;
2108c2ecf20Sopenharmony_ci	if (err < 0)
2118c2ecf20Sopenharmony_ci		goto end;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	params->clk_src		= buf[6];
2148c2ecf20Sopenharmony_ci	params->dig_in_fmt	= buf[7];
2158c2ecf20Sopenharmony_ci	params->dig_out_fmt	= buf[8];
2168c2ecf20Sopenharmony_ci	params->clk_lock	= buf[9];
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	if (params->ctl_id_sync)
2198c2ecf20Sopenharmony_ci		snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
2208c2ecf20Sopenharmony_ci			       params->ctl_id_sync);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	err = 0;
2238c2ecf20Sopenharmony_ciend:
2248c2ecf20Sopenharmony_ci	kfree(buf);
2258c2ecf20Sopenharmony_ci	return err;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_cistatic void
2288c2ecf20Sopenharmony_cispecial_stream_formation_set(struct snd_bebob *bebob)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	static const unsigned int ch_table[2][2][3] = {
2318c2ecf20Sopenharmony_ci		/* AMDTP_OUT_STREAM */
2328c2ecf20Sopenharmony_ci		{ {  6,  6,  4 },	/* SPDIF */
2338c2ecf20Sopenharmony_ci		  { 12,  8,  4 } },	/* ADAT */
2348c2ecf20Sopenharmony_ci		/* AMDTP_IN_STREAM */
2358c2ecf20Sopenharmony_ci		{ { 10, 10,  2 },	/* SPDIF */
2368c2ecf20Sopenharmony_ci		  { 16, 12,  2 } }	/* ADAT */
2378c2ecf20Sopenharmony_ci	};
2388c2ecf20Sopenharmony_ci	struct special_params *params = bebob->maudio_special_quirk;
2398c2ecf20Sopenharmony_ci	unsigned int i, max;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	max = SND_BEBOB_STRM_FMT_ENTRIES - 1;
2428c2ecf20Sopenharmony_ci	if (!params->is1814)
2438c2ecf20Sopenharmony_ci		max -= 2;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	for (i = 0; i < max; i++) {
2468c2ecf20Sopenharmony_ci		bebob->tx_stream_formations[i + 1].pcm =
2478c2ecf20Sopenharmony_ci			ch_table[AMDTP_IN_STREAM][params->dig_in_fmt][i / 2];
2488c2ecf20Sopenharmony_ci		bebob->tx_stream_formations[i + 1].midi = 1;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci		bebob->rx_stream_formations[i + 1].pcm =
2518c2ecf20Sopenharmony_ci			ch_table[AMDTP_OUT_STREAM][params->dig_out_fmt][i / 2];
2528c2ecf20Sopenharmony_ci		bebob->rx_stream_formations[i + 1].midi = 1;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic int add_special_controls(struct snd_bebob *bebob);
2578c2ecf20Sopenharmony_ciint
2588c2ecf20Sopenharmony_cisnd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct special_params *params;
2618c2ecf20Sopenharmony_ci	int err;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	params = devm_kzalloc(&bebob->card->card_dev,
2648c2ecf20Sopenharmony_ci			      sizeof(struct special_params), GFP_KERNEL);
2658c2ecf20Sopenharmony_ci	if (!params)
2668c2ecf20Sopenharmony_ci		return -ENOMEM;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	mutex_lock(&bebob->mutex);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	bebob->maudio_special_quirk = (void *)params;
2718c2ecf20Sopenharmony_ci	params->is1814 = is1814;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/* initialize these parameters because driver is not allowed to ask */
2748c2ecf20Sopenharmony_ci	bebob->rx_stream.context = ERR_PTR(-1);
2758c2ecf20Sopenharmony_ci	bebob->tx_stream.context = ERR_PTR(-1);
2768c2ecf20Sopenharmony_ci	err = avc_maudio_set_special_clk(bebob, 0x03, 0x00, 0x00, 0x00);
2778c2ecf20Sopenharmony_ci	if (err < 0) {
2788c2ecf20Sopenharmony_ci		dev_err(&bebob->unit->device,
2798c2ecf20Sopenharmony_ci			"fail to initialize clock params: %d\n", err);
2808c2ecf20Sopenharmony_ci		goto end;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	err = add_special_controls(bebob);
2848c2ecf20Sopenharmony_ci	if (err < 0)
2858c2ecf20Sopenharmony_ci		goto end;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	special_stream_formation_set(bebob);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (params->is1814) {
2908c2ecf20Sopenharmony_ci		bebob->midi_input_ports = 1;
2918c2ecf20Sopenharmony_ci		bebob->midi_output_ports = 1;
2928c2ecf20Sopenharmony_ci	} else {
2938c2ecf20Sopenharmony_ci		bebob->midi_input_ports = 2;
2948c2ecf20Sopenharmony_ci		bebob->midi_output_ports = 2;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ciend:
2978c2ecf20Sopenharmony_ci	mutex_unlock(&bebob->mutex);
2988c2ecf20Sopenharmony_ci	return err;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci/* Input plug shows actual rate. Output plug is needless for this purpose. */
3028c2ecf20Sopenharmony_cistatic int special_get_rate(struct snd_bebob *bebob, unsigned int *rate)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	int err, trials;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	trials = 0;
3078c2ecf20Sopenharmony_ci	do {
3088c2ecf20Sopenharmony_ci		err = avc_general_get_sig_fmt(bebob->unit, rate,
3098c2ecf20Sopenharmony_ci					      AVC_GENERAL_PLUG_DIR_IN, 0);
3108c2ecf20Sopenharmony_ci	} while (err == -EAGAIN && ++trials < 3);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	return err;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_cistatic int special_set_rate(struct snd_bebob *bebob, unsigned int rate)
3158c2ecf20Sopenharmony_ci{
3168c2ecf20Sopenharmony_ci	struct special_params *params = bebob->maudio_special_quirk;
3178c2ecf20Sopenharmony_ci	int err;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	err = avc_general_set_sig_fmt(bebob->unit, rate,
3208c2ecf20Sopenharmony_ci				      AVC_GENERAL_PLUG_DIR_OUT, 0);
3218c2ecf20Sopenharmony_ci	if (err < 0)
3228c2ecf20Sopenharmony_ci		goto end;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci	/*
3258c2ecf20Sopenharmony_ci	 * Just after changing sampling rate for output, a followed command
3268c2ecf20Sopenharmony_ci	 * for input is easy to fail. This is a workaround fot this issue.
3278c2ecf20Sopenharmony_ci	 */
3288c2ecf20Sopenharmony_ci	msleep(100);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	err = avc_general_set_sig_fmt(bebob->unit, rate,
3318c2ecf20Sopenharmony_ci				      AVC_GENERAL_PLUG_DIR_IN, 0);
3328c2ecf20Sopenharmony_ci	if (err < 0)
3338c2ecf20Sopenharmony_ci		goto end;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (params->ctl_id_sync)
3368c2ecf20Sopenharmony_ci		snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
3378c2ecf20Sopenharmony_ci			       params->ctl_id_sync);
3388c2ecf20Sopenharmony_ciend:
3398c2ecf20Sopenharmony_ci	return err;
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci/* Clock source control for special firmware */
3438c2ecf20Sopenharmony_cistatic const enum snd_bebob_clock_type special_clk_types[] = {
3448c2ecf20Sopenharmony_ci	SND_BEBOB_CLOCK_TYPE_INTERNAL,	/* With digital mute */
3458c2ecf20Sopenharmony_ci	SND_BEBOB_CLOCK_TYPE_EXTERNAL,	/* SPDIF/ADAT */
3468c2ecf20Sopenharmony_ci	SND_BEBOB_CLOCK_TYPE_EXTERNAL,	/* Word Clock */
3478c2ecf20Sopenharmony_ci	SND_BEBOB_CLOCK_TYPE_INTERNAL,
3488c2ecf20Sopenharmony_ci};
3498c2ecf20Sopenharmony_cistatic int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
3508c2ecf20Sopenharmony_ci{
3518c2ecf20Sopenharmony_ci	struct special_params *params = bebob->maudio_special_quirk;
3528c2ecf20Sopenharmony_ci	*id = params->clk_src;
3538c2ecf20Sopenharmony_ci	return 0;
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_cistatic int special_clk_ctl_info(struct snd_kcontrol *kctl,
3568c2ecf20Sopenharmony_ci				struct snd_ctl_elem_info *einf)
3578c2ecf20Sopenharmony_ci{
3588c2ecf20Sopenharmony_ci	static const char *const special_clk_labels[] = {
3598c2ecf20Sopenharmony_ci		"Internal with Digital Mute",
3608c2ecf20Sopenharmony_ci		"Digital",
3618c2ecf20Sopenharmony_ci		"Word Clock",
3628c2ecf20Sopenharmony_ci		"Internal"
3638c2ecf20Sopenharmony_ci	};
3648c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_types),
3658c2ecf20Sopenharmony_ci				 special_clk_labels);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_cistatic int special_clk_ctl_get(struct snd_kcontrol *kctl,
3688c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *uval)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
3718c2ecf20Sopenharmony_ci	struct special_params *params = bebob->maudio_special_quirk;
3728c2ecf20Sopenharmony_ci	uval->value.enumerated.item[0] = params->clk_src;
3738c2ecf20Sopenharmony_ci	return 0;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_cistatic int special_clk_ctl_put(struct snd_kcontrol *kctl,
3768c2ecf20Sopenharmony_ci			       struct snd_ctl_elem_value *uval)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
3798c2ecf20Sopenharmony_ci	struct special_params *params = bebob->maudio_special_quirk;
3808c2ecf20Sopenharmony_ci	int err, id;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	id = uval->value.enumerated.item[0];
3838c2ecf20Sopenharmony_ci	if (id >= ARRAY_SIZE(special_clk_types))
3848c2ecf20Sopenharmony_ci		return -EINVAL;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	mutex_lock(&bebob->mutex);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	err = avc_maudio_set_special_clk(bebob, id,
3898c2ecf20Sopenharmony_ci					 params->dig_in_fmt,
3908c2ecf20Sopenharmony_ci					 params->dig_out_fmt,
3918c2ecf20Sopenharmony_ci					 params->clk_lock);
3928c2ecf20Sopenharmony_ci	mutex_unlock(&bebob->mutex);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	if (err >= 0)
3958c2ecf20Sopenharmony_ci		err = 1;
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	return err;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new special_clk_ctl = {
4008c2ecf20Sopenharmony_ci	.name	= "Clock Source",
4018c2ecf20Sopenharmony_ci	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
4028c2ecf20Sopenharmony_ci	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,
4038c2ecf20Sopenharmony_ci	.info	= special_clk_ctl_info,
4048c2ecf20Sopenharmony_ci	.get	= special_clk_ctl_get,
4058c2ecf20Sopenharmony_ci	.put	= special_clk_ctl_put
4068c2ecf20Sopenharmony_ci};
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci/* Clock synchronization control for special firmware */
4098c2ecf20Sopenharmony_cistatic int special_sync_ctl_info(struct snd_kcontrol *kctl,
4108c2ecf20Sopenharmony_ci				 struct snd_ctl_elem_info *einf)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	einf->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
4138c2ecf20Sopenharmony_ci	einf->count = 1;
4148c2ecf20Sopenharmony_ci	einf->value.integer.min = 0;
4158c2ecf20Sopenharmony_ci	einf->value.integer.max = 1;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	return 0;
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_cistatic int special_sync_ctl_get(struct snd_kcontrol *kctl,
4208c2ecf20Sopenharmony_ci				struct snd_ctl_elem_value *uval)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
4238c2ecf20Sopenharmony_ci	int err;
4248c2ecf20Sopenharmony_ci	bool synced = 0;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	err = check_clk_sync(bebob, METER_SIZE_SPECIAL, &synced);
4278c2ecf20Sopenharmony_ci	if (err >= 0)
4288c2ecf20Sopenharmony_ci		uval->value.integer.value[0] = synced;
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	return 0;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new special_sync_ctl = {
4338c2ecf20Sopenharmony_ci	.name	= "Sync Status",
4348c2ecf20Sopenharmony_ci	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
4358c2ecf20Sopenharmony_ci	.access	= SNDRV_CTL_ELEM_ACCESS_READ,
4368c2ecf20Sopenharmony_ci	.info	= special_sync_ctl_info,
4378c2ecf20Sopenharmony_ci	.get	= special_sync_ctl_get,
4388c2ecf20Sopenharmony_ci};
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci/* Digital input interface control for special firmware */
4418c2ecf20Sopenharmony_cistatic const char *const special_dig_in_iface_labels[] = {
4428c2ecf20Sopenharmony_ci	"S/PDIF Optical", "S/PDIF Coaxial", "ADAT Optical"
4438c2ecf20Sopenharmony_ci};
4448c2ecf20Sopenharmony_cistatic int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl,
4458c2ecf20Sopenharmony_ci					 struct snd_ctl_elem_info *einf)
4468c2ecf20Sopenharmony_ci{
4478c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(einf, 1,
4488c2ecf20Sopenharmony_ci				 ARRAY_SIZE(special_dig_in_iface_labels),
4498c2ecf20Sopenharmony_ci				 special_dig_in_iface_labels);
4508c2ecf20Sopenharmony_ci}
4518c2ecf20Sopenharmony_cistatic int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl,
4528c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *uval)
4538c2ecf20Sopenharmony_ci{
4548c2ecf20Sopenharmony_ci	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
4558c2ecf20Sopenharmony_ci	struct special_params *params = bebob->maudio_special_quirk;
4568c2ecf20Sopenharmony_ci	unsigned int dig_in_iface;
4578c2ecf20Sopenharmony_ci	int err, val;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	mutex_lock(&bebob->mutex);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	err = avc_audio_get_selector(bebob->unit, 0x00, 0x04,
4628c2ecf20Sopenharmony_ci				     &dig_in_iface);
4638c2ecf20Sopenharmony_ci	if (err < 0) {
4648c2ecf20Sopenharmony_ci		dev_err(&bebob->unit->device,
4658c2ecf20Sopenharmony_ci			"fail to get digital input interface: %d\n", err);
4668c2ecf20Sopenharmony_ci		goto end;
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	/* encoded id for user value */
4708c2ecf20Sopenharmony_ci	val = (params->dig_in_fmt << 1) | (dig_in_iface & 0x01);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	/* for ADAT Optical */
4738c2ecf20Sopenharmony_ci	if (val > 2)
4748c2ecf20Sopenharmony_ci		val = 2;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	uval->value.enumerated.item[0] = val;
4778c2ecf20Sopenharmony_ciend:
4788c2ecf20Sopenharmony_ci	mutex_unlock(&bebob->mutex);
4798c2ecf20Sopenharmony_ci	return err;
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_cistatic int special_dig_in_iface_ctl_set(struct snd_kcontrol *kctl,
4828c2ecf20Sopenharmony_ci					struct snd_ctl_elem_value *uval)
4838c2ecf20Sopenharmony_ci{
4848c2ecf20Sopenharmony_ci	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
4858c2ecf20Sopenharmony_ci	struct special_params *params = bebob->maudio_special_quirk;
4868c2ecf20Sopenharmony_ci	unsigned int id, dig_in_fmt, dig_in_iface;
4878c2ecf20Sopenharmony_ci	int err;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	id = uval->value.enumerated.item[0];
4908c2ecf20Sopenharmony_ci	if (id >= ARRAY_SIZE(special_dig_in_iface_labels))
4918c2ecf20Sopenharmony_ci		return -EINVAL;
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* decode user value */
4948c2ecf20Sopenharmony_ci	dig_in_fmt = (id >> 1) & 0x01;
4958c2ecf20Sopenharmony_ci	dig_in_iface = id & 0x01;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	mutex_lock(&bebob->mutex);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	err = avc_maudio_set_special_clk(bebob,
5008c2ecf20Sopenharmony_ci					 params->clk_src,
5018c2ecf20Sopenharmony_ci					 dig_in_fmt,
5028c2ecf20Sopenharmony_ci					 params->dig_out_fmt,
5038c2ecf20Sopenharmony_ci					 params->clk_lock);
5048c2ecf20Sopenharmony_ci	if (err < 0)
5058c2ecf20Sopenharmony_ci		goto end;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	/* For ADAT, optical interface is only available. */
5088c2ecf20Sopenharmony_ci	if (params->dig_in_fmt > 0) {
5098c2ecf20Sopenharmony_ci		err = 1;
5108c2ecf20Sopenharmony_ci		goto end;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	/* For S/PDIF, optical/coaxial interfaces are selectable. */
5148c2ecf20Sopenharmony_ci	err = avc_audio_set_selector(bebob->unit, 0x00, 0x04, dig_in_iface);
5158c2ecf20Sopenharmony_ci	if (err < 0)
5168c2ecf20Sopenharmony_ci		dev_err(&bebob->unit->device,
5178c2ecf20Sopenharmony_ci			"fail to set digital input interface: %d\n", err);
5188c2ecf20Sopenharmony_ci	err = 1;
5198c2ecf20Sopenharmony_ciend:
5208c2ecf20Sopenharmony_ci	special_stream_formation_set(bebob);
5218c2ecf20Sopenharmony_ci	mutex_unlock(&bebob->mutex);
5228c2ecf20Sopenharmony_ci	return err;
5238c2ecf20Sopenharmony_ci}
5248c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new special_dig_in_iface_ctl = {
5258c2ecf20Sopenharmony_ci	.name	= "Digital Input Interface",
5268c2ecf20Sopenharmony_ci	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
5278c2ecf20Sopenharmony_ci	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,
5288c2ecf20Sopenharmony_ci	.info	= special_dig_in_iface_ctl_info,
5298c2ecf20Sopenharmony_ci	.get	= special_dig_in_iface_ctl_get,
5308c2ecf20Sopenharmony_ci	.put	= special_dig_in_iface_ctl_set
5318c2ecf20Sopenharmony_ci};
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci/* Digital output interface control for special firmware */
5348c2ecf20Sopenharmony_cistatic const char *const special_dig_out_iface_labels[] = {
5358c2ecf20Sopenharmony_ci	"S/PDIF Optical and Coaxial", "ADAT Optical"
5368c2ecf20Sopenharmony_ci};
5378c2ecf20Sopenharmony_cistatic int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl,
5388c2ecf20Sopenharmony_ci					  struct snd_ctl_elem_info *einf)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	return snd_ctl_enum_info(einf, 1,
5418c2ecf20Sopenharmony_ci				 ARRAY_SIZE(special_dig_out_iface_labels),
5428c2ecf20Sopenharmony_ci				 special_dig_out_iface_labels);
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_cistatic int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl,
5458c2ecf20Sopenharmony_ci					 struct snd_ctl_elem_value *uval)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
5488c2ecf20Sopenharmony_ci	struct special_params *params = bebob->maudio_special_quirk;
5498c2ecf20Sopenharmony_ci	mutex_lock(&bebob->mutex);
5508c2ecf20Sopenharmony_ci	uval->value.enumerated.item[0] = params->dig_out_fmt;
5518c2ecf20Sopenharmony_ci	mutex_unlock(&bebob->mutex);
5528c2ecf20Sopenharmony_ci	return 0;
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_cistatic int special_dig_out_iface_ctl_set(struct snd_kcontrol *kctl,
5558c2ecf20Sopenharmony_ci					 struct snd_ctl_elem_value *uval)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
5588c2ecf20Sopenharmony_ci	struct special_params *params = bebob->maudio_special_quirk;
5598c2ecf20Sopenharmony_ci	unsigned int id;
5608c2ecf20Sopenharmony_ci	int err;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	id = uval->value.enumerated.item[0];
5638c2ecf20Sopenharmony_ci	if (id >= ARRAY_SIZE(special_dig_out_iface_labels))
5648c2ecf20Sopenharmony_ci		return -EINVAL;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	mutex_lock(&bebob->mutex);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	err = avc_maudio_set_special_clk(bebob,
5698c2ecf20Sopenharmony_ci					 params->clk_src,
5708c2ecf20Sopenharmony_ci					 params->dig_in_fmt,
5718c2ecf20Sopenharmony_ci					 id, params->clk_lock);
5728c2ecf20Sopenharmony_ci	if (err >= 0) {
5738c2ecf20Sopenharmony_ci		special_stream_formation_set(bebob);
5748c2ecf20Sopenharmony_ci		err = 1;
5758c2ecf20Sopenharmony_ci	}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	mutex_unlock(&bebob->mutex);
5788c2ecf20Sopenharmony_ci	return err;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new special_dig_out_iface_ctl = {
5818c2ecf20Sopenharmony_ci	.name	= "Digital Output Interface",
5828c2ecf20Sopenharmony_ci	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
5838c2ecf20Sopenharmony_ci	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,
5848c2ecf20Sopenharmony_ci	.info	= special_dig_out_iface_ctl_info,
5858c2ecf20Sopenharmony_ci	.get	= special_dig_out_iface_ctl_get,
5868c2ecf20Sopenharmony_ci	.put	= special_dig_out_iface_ctl_set
5878c2ecf20Sopenharmony_ci};
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic int add_special_controls(struct snd_bebob *bebob)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	struct snd_kcontrol *kctl;
5928c2ecf20Sopenharmony_ci	struct special_params *params = bebob->maudio_special_quirk;
5938c2ecf20Sopenharmony_ci	int err;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	kctl = snd_ctl_new1(&special_clk_ctl, bebob);
5968c2ecf20Sopenharmony_ci	err = snd_ctl_add(bebob->card, kctl);
5978c2ecf20Sopenharmony_ci	if (err < 0)
5988c2ecf20Sopenharmony_ci		goto end;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	kctl = snd_ctl_new1(&special_sync_ctl, bebob);
6018c2ecf20Sopenharmony_ci	err = snd_ctl_add(bebob->card, kctl);
6028c2ecf20Sopenharmony_ci	if (err < 0)
6038c2ecf20Sopenharmony_ci		goto end;
6048c2ecf20Sopenharmony_ci	params->ctl_id_sync = &kctl->id;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	kctl = snd_ctl_new1(&special_dig_in_iface_ctl, bebob);
6078c2ecf20Sopenharmony_ci	err = snd_ctl_add(bebob->card, kctl);
6088c2ecf20Sopenharmony_ci	if (err < 0)
6098c2ecf20Sopenharmony_ci		goto end;
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	kctl = snd_ctl_new1(&special_dig_out_iface_ctl, bebob);
6128c2ecf20Sopenharmony_ci	err = snd_ctl_add(bebob->card, kctl);
6138c2ecf20Sopenharmony_ciend:
6148c2ecf20Sopenharmony_ci	return err;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci/* Hardware metering for special firmware */
6188c2ecf20Sopenharmony_cistatic const char *const special_meter_labels[] = {
6198c2ecf20Sopenharmony_ci	ANA_IN, ANA_IN, ANA_IN, ANA_IN,
6208c2ecf20Sopenharmony_ci	SPDIF_IN,
6218c2ecf20Sopenharmony_ci	ADAT_IN, ADAT_IN, ADAT_IN, ADAT_IN,
6228c2ecf20Sopenharmony_ci	ANA_OUT, ANA_OUT,
6238c2ecf20Sopenharmony_ci	SPDIF_OUT,
6248c2ecf20Sopenharmony_ci	ADAT_OUT, ADAT_OUT, ADAT_OUT, ADAT_OUT,
6258c2ecf20Sopenharmony_ci	HP_OUT, HP_OUT,
6268c2ecf20Sopenharmony_ci	AUX_OUT
6278c2ecf20Sopenharmony_ci};
6288c2ecf20Sopenharmony_cistatic int
6298c2ecf20Sopenharmony_cispecial_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	__be16 *buf;
6328c2ecf20Sopenharmony_ci	unsigned int i, c, channels;
6338c2ecf20Sopenharmony_ci	int err;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	channels = ARRAY_SIZE(special_meter_labels) * 2;
6368c2ecf20Sopenharmony_ci	if (size < channels * sizeof(u32))
6378c2ecf20Sopenharmony_ci		return -EINVAL;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	/* omit last 4 bytes because it's clock info. */
6408c2ecf20Sopenharmony_ci	buf = kmalloc(METER_SIZE_SPECIAL - 4, GFP_KERNEL);
6418c2ecf20Sopenharmony_ci	if (buf == NULL)
6428c2ecf20Sopenharmony_ci		return -ENOMEM;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	err = get_meter(bebob, (void *)buf, METER_SIZE_SPECIAL - 4);
6458c2ecf20Sopenharmony_ci	if (err < 0)
6468c2ecf20Sopenharmony_ci		goto end;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	/* Its format is u16 and some channels are unknown. */
6498c2ecf20Sopenharmony_ci	i = 0;
6508c2ecf20Sopenharmony_ci	for (c = 2; c < channels + 2; c++)
6518c2ecf20Sopenharmony_ci		target[i++] = be16_to_cpu(buf[c]) << 16;
6528c2ecf20Sopenharmony_ciend:
6538c2ecf20Sopenharmony_ci	kfree(buf);
6548c2ecf20Sopenharmony_ci	return err;
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci/* last 4 bytes are omitted because it's clock info. */
6588c2ecf20Sopenharmony_cistatic const char *const fw410_meter_labels[] = {
6598c2ecf20Sopenharmony_ci	ANA_IN, DIG_IN,
6608c2ecf20Sopenharmony_ci	ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT,
6618c2ecf20Sopenharmony_ci	HP_OUT
6628c2ecf20Sopenharmony_ci};
6638c2ecf20Sopenharmony_cistatic const char *const audiophile_meter_labels[] = {
6648c2ecf20Sopenharmony_ci	ANA_IN, DIG_IN,
6658c2ecf20Sopenharmony_ci	ANA_OUT, ANA_OUT, DIG_OUT,
6668c2ecf20Sopenharmony_ci	HP_OUT, AUX_OUT,
6678c2ecf20Sopenharmony_ci};
6688c2ecf20Sopenharmony_cistatic const char *const solo_meter_labels[] = {
6698c2ecf20Sopenharmony_ci	ANA_IN, DIG_IN,
6708c2ecf20Sopenharmony_ci	STRM_IN, STRM_IN,
6718c2ecf20Sopenharmony_ci	ANA_OUT, DIG_OUT
6728c2ecf20Sopenharmony_ci};
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci/* no clock info */
6758c2ecf20Sopenharmony_cistatic const char *const ozonic_meter_labels[] = {
6768c2ecf20Sopenharmony_ci	ANA_IN, ANA_IN,
6778c2ecf20Sopenharmony_ci	STRM_IN, STRM_IN,
6788c2ecf20Sopenharmony_ci	ANA_OUT, ANA_OUT
6798c2ecf20Sopenharmony_ci};
6808c2ecf20Sopenharmony_ci/* TODO: need testers. these positions are based on authour's assumption */
6818c2ecf20Sopenharmony_cistatic const char *const nrv10_meter_labels[] = {
6828c2ecf20Sopenharmony_ci	ANA_IN, ANA_IN, ANA_IN, ANA_IN,
6838c2ecf20Sopenharmony_ci	DIG_IN,
6848c2ecf20Sopenharmony_ci	ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
6858c2ecf20Sopenharmony_ci	DIG_IN
6868c2ecf20Sopenharmony_ci};
6878c2ecf20Sopenharmony_cistatic int
6888c2ecf20Sopenharmony_cinormal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
6898c2ecf20Sopenharmony_ci{
6908c2ecf20Sopenharmony_ci	const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
6918c2ecf20Sopenharmony_ci	unsigned int c, channels;
6928c2ecf20Sopenharmony_ci	int err;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	channels = spec->num * 2;
6958c2ecf20Sopenharmony_ci	if (size < channels * sizeof(u32))
6968c2ecf20Sopenharmony_ci		return -EINVAL;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	err = get_meter(bebob, (void *)buf, size);
6998c2ecf20Sopenharmony_ci	if (err < 0)
7008c2ecf20Sopenharmony_ci		goto end;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	for (c = 0; c < channels; c++)
7038c2ecf20Sopenharmony_ci		be32_to_cpus(&buf[c]);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	/* swap stream channels because inverted */
7068c2ecf20Sopenharmony_ci	if (spec->labels == solo_meter_labels) {
7078c2ecf20Sopenharmony_ci		swap(buf[4], buf[6]);
7088c2ecf20Sopenharmony_ci		swap(buf[5], buf[7]);
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ciend:
7118c2ecf20Sopenharmony_ci	return err;
7128c2ecf20Sopenharmony_ci}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci/* for special customized devices */
7158c2ecf20Sopenharmony_cistatic const struct snd_bebob_rate_spec special_rate_spec = {
7168c2ecf20Sopenharmony_ci	.get	= &special_get_rate,
7178c2ecf20Sopenharmony_ci	.set	= &special_set_rate,
7188c2ecf20Sopenharmony_ci};
7198c2ecf20Sopenharmony_cistatic const struct snd_bebob_clock_spec special_clk_spec = {
7208c2ecf20Sopenharmony_ci	.num	= ARRAY_SIZE(special_clk_types),
7218c2ecf20Sopenharmony_ci	.types	= special_clk_types,
7228c2ecf20Sopenharmony_ci	.get	= &special_clk_get,
7238c2ecf20Sopenharmony_ci};
7248c2ecf20Sopenharmony_cistatic const struct snd_bebob_meter_spec special_meter_spec = {
7258c2ecf20Sopenharmony_ci	.num	= ARRAY_SIZE(special_meter_labels),
7268c2ecf20Sopenharmony_ci	.labels	= special_meter_labels,
7278c2ecf20Sopenharmony_ci	.get	= &special_meter_get
7288c2ecf20Sopenharmony_ci};
7298c2ecf20Sopenharmony_ciconst struct snd_bebob_spec maudio_special_spec = {
7308c2ecf20Sopenharmony_ci	.clock	= &special_clk_spec,
7318c2ecf20Sopenharmony_ci	.rate	= &special_rate_spec,
7328c2ecf20Sopenharmony_ci	.meter	= &special_meter_spec
7338c2ecf20Sopenharmony_ci};
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci/* Firewire 410 specification */
7368c2ecf20Sopenharmony_cistatic const struct snd_bebob_rate_spec usual_rate_spec = {
7378c2ecf20Sopenharmony_ci	.get	= &snd_bebob_stream_get_rate,
7388c2ecf20Sopenharmony_ci	.set	= &snd_bebob_stream_set_rate,
7398c2ecf20Sopenharmony_ci};
7408c2ecf20Sopenharmony_cistatic const struct snd_bebob_meter_spec fw410_meter_spec = {
7418c2ecf20Sopenharmony_ci	.num	= ARRAY_SIZE(fw410_meter_labels),
7428c2ecf20Sopenharmony_ci	.labels	= fw410_meter_labels,
7438c2ecf20Sopenharmony_ci	.get	= &normal_meter_get
7448c2ecf20Sopenharmony_ci};
7458c2ecf20Sopenharmony_ciconst struct snd_bebob_spec maudio_fw410_spec = {
7468c2ecf20Sopenharmony_ci	.clock	= NULL,
7478c2ecf20Sopenharmony_ci	.rate	= &usual_rate_spec,
7488c2ecf20Sopenharmony_ci	.meter	= &fw410_meter_spec
7498c2ecf20Sopenharmony_ci};
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci/* Firewire Audiophile specification */
7528c2ecf20Sopenharmony_cistatic const struct snd_bebob_meter_spec audiophile_meter_spec = {
7538c2ecf20Sopenharmony_ci	.num	= ARRAY_SIZE(audiophile_meter_labels),
7548c2ecf20Sopenharmony_ci	.labels	= audiophile_meter_labels,
7558c2ecf20Sopenharmony_ci	.get	= &normal_meter_get
7568c2ecf20Sopenharmony_ci};
7578c2ecf20Sopenharmony_ciconst struct snd_bebob_spec maudio_audiophile_spec = {
7588c2ecf20Sopenharmony_ci	.clock	= NULL,
7598c2ecf20Sopenharmony_ci	.rate	= &usual_rate_spec,
7608c2ecf20Sopenharmony_ci	.meter	= &audiophile_meter_spec
7618c2ecf20Sopenharmony_ci};
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci/* Firewire Solo specification */
7648c2ecf20Sopenharmony_cistatic const struct snd_bebob_meter_spec solo_meter_spec = {
7658c2ecf20Sopenharmony_ci	.num	= ARRAY_SIZE(solo_meter_labels),
7668c2ecf20Sopenharmony_ci	.labels	= solo_meter_labels,
7678c2ecf20Sopenharmony_ci	.get	= &normal_meter_get
7688c2ecf20Sopenharmony_ci};
7698c2ecf20Sopenharmony_ciconst struct snd_bebob_spec maudio_solo_spec = {
7708c2ecf20Sopenharmony_ci	.clock	= NULL,
7718c2ecf20Sopenharmony_ci	.rate	= &usual_rate_spec,
7728c2ecf20Sopenharmony_ci	.meter	= &solo_meter_spec
7738c2ecf20Sopenharmony_ci};
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci/* Ozonic specification */
7768c2ecf20Sopenharmony_cistatic const struct snd_bebob_meter_spec ozonic_meter_spec = {
7778c2ecf20Sopenharmony_ci	.num	= ARRAY_SIZE(ozonic_meter_labels),
7788c2ecf20Sopenharmony_ci	.labels	= ozonic_meter_labels,
7798c2ecf20Sopenharmony_ci	.get	= &normal_meter_get
7808c2ecf20Sopenharmony_ci};
7818c2ecf20Sopenharmony_ciconst struct snd_bebob_spec maudio_ozonic_spec = {
7828c2ecf20Sopenharmony_ci	.clock	= NULL,
7838c2ecf20Sopenharmony_ci	.rate	= &usual_rate_spec,
7848c2ecf20Sopenharmony_ci	.meter	= &ozonic_meter_spec
7858c2ecf20Sopenharmony_ci};
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci/* NRV10 specification */
7888c2ecf20Sopenharmony_cistatic const struct snd_bebob_meter_spec nrv10_meter_spec = {
7898c2ecf20Sopenharmony_ci	.num	= ARRAY_SIZE(nrv10_meter_labels),
7908c2ecf20Sopenharmony_ci	.labels	= nrv10_meter_labels,
7918c2ecf20Sopenharmony_ci	.get	= &normal_meter_get
7928c2ecf20Sopenharmony_ci};
7938c2ecf20Sopenharmony_ciconst struct snd_bebob_spec maudio_nrv10_spec = {
7948c2ecf20Sopenharmony_ci	.clock	= NULL,
7958c2ecf20Sopenharmony_ci	.rate	= &usual_rate_spec,
7968c2ecf20Sopenharmony_ci	.meter	= &nrv10_meter_spec
7978c2ecf20Sopenharmony_ci};
798