brintos

brintos / linux-shallow public Read only

0
0
Text · 21.1 KiB · 177699e Raw
798 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * bebob_maudio.c - a part of driver for BeBoB based devices4 *5 * Copyright (c) 2013-2014 Takashi Sakamoto6 */7 8#include "./bebob.h"9#include <sound/control.h>10 11/*12 * Just powering on, Firewire 410/Audiophile/1814 and ProjectMix I/O wait to13 * download firmware blob. To enable these devices, drivers should upload14 * firmware blob and send a command to initialize configuration to factory15 * settings when completing uploading. Then these devices generate bus reset16 * and are recognized as new devices with the firmware.17 *18 * But with firmware version 5058 or later, the firmware is stored to flash19 * memory in the device and drivers can tell bootloader to load the firmware20 * by sending a cue. This cue must be sent one time.21 *22 * For streaming, both of output and input streams are needed for Firewire 41023 * and Ozonic. The single stream is OK for the other devices even if the clock24 * source is not SYT-Match (I note no devices use SYT-Match).25 *26 * Without streaming, the devices except for Firewire Audiophile can mix any27 * input and output. For this reason, Audiophile cannot be used as standalone28 * mixer.29 *30 * Firewire 1814 and ProjectMix I/O uses special firmware. It will be freezed31 * when receiving any commands which the firmware can't understand. These32 * devices utilize completely different system to control. It is some33 * write-transaction directly into a certain address. All of addresses for mixer34 * functionality is between 0xffc700700000 to 0xffc70070009c.35 */36 37/* Offset from information register */38#define INFO_OFFSET_SW_DATE	0x2039 40/* Bootloader Protocol Version 1 */41#define MAUDIO_BOOTLOADER_CUE1	0x0000000142/*43 * Initializing configuration to factory settings (= 0x1101), (swapped in line),44 * Command code is zero (= 0x00),45 * the number of operands is zero (= 0x00)(at least significant byte)46 */47#define MAUDIO_BOOTLOADER_CUE2	0x0111000048/* padding */49#define MAUDIO_BOOTLOADER_CUE3	0x0000000050 51#define MAUDIO_SPECIFIC_ADDRESS	0xffc700000000ULL52 53#define METER_OFFSET		0x0060000054 55/* some device has sync info after metering data */56#define METER_SIZE_SPECIAL	84	/* with sync info */57#define METER_SIZE_FW410	76	/* with sync info */58#define METER_SIZE_AUDIOPHILE	60	/* with sync info */59#define METER_SIZE_SOLO		52	/* with sync info */60#define METER_SIZE_OZONIC	4861#define METER_SIZE_NRV10	8062 63/* labels for metering */64#define ANA_IN		"Analog In"65#define ANA_OUT		"Analog Out"66#define DIG_IN		"Digital In"67#define SPDIF_IN	"S/PDIF In"68#define ADAT_IN		"ADAT In"69#define DIG_OUT		"Digital Out"70#define SPDIF_OUT	"S/PDIF Out"71#define ADAT_OUT	"ADAT Out"72#define STRM_IN		"Stream In"73#define AUX_OUT		"Aux Out"74#define HP_OUT		"HP Out"75/* for NRV */76#define UNKNOWN_METER	"Unknown"77 78struct special_params {79	bool is1814;80	unsigned int clk_src;81	unsigned int dig_in_fmt;82	unsigned int dig_out_fmt;83	unsigned int clk_lock;84	struct snd_ctl_elem_id *ctl_id_sync;85};86 87/*88 * For some M-Audio devices, this module just send cue to load firmware. After89 * loading, the device generates bus reset and newly detected.90 *91 * If we make any transactions to load firmware, the operation may failed.92 */93int snd_bebob_maudio_load_firmware(struct fw_unit *unit)94{95	struct fw_device *device = fw_parent_device(unit);96	int err, rcode;97	u64 date;98	__le32 *cues;99 100	/* check date of software used to build */101	err = snd_bebob_read_block(unit, INFO_OFFSET_SW_DATE,102				   &date, sizeof(u64));103	if (err < 0)104		return err;105	/*106	 * firmware version 5058 or later has date later than "20070401", but107	 * 'date' is not null-terminated.108	 */109	if (date < 0x3230303730343031LL) {110		dev_err(&unit->device,111			"Use firmware version 5058 or later\n");112		return -ENXIO;113	}114 115	cues = kmalloc_array(3, sizeof(*cues), GFP_KERNEL);116	if (!cues)117		return -ENOMEM;118 119	cues[0] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE1);120	cues[1] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE2);121	cues[2] = cpu_to_le32(MAUDIO_BOOTLOADER_CUE3);122 123	rcode = fw_run_transaction(device->card, TCODE_WRITE_BLOCK_REQUEST,124				   device->node_id, device->generation,125				   device->max_speed, BEBOB_ADDR_REG_REQ,126				   cues, 3 * sizeof(*cues));127	kfree(cues);128	if (rcode != RCODE_COMPLETE) {129		dev_err(&unit->device,130			"Failed to send a cue to load firmware\n");131		err = -EIO;132	}133 134	return err;135}136 137static inline int138get_meter(struct snd_bebob *bebob, void *buf, unsigned int size)139{140	return snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,141				  MAUDIO_SPECIFIC_ADDRESS + METER_OFFSET,142				  buf, size, 0);143}144 145static int146check_clk_sync(struct snd_bebob *bebob, unsigned int size, bool *sync)147{148	int err;149	u8 *buf;150 151	buf = kmalloc(size, GFP_KERNEL);152	if (buf == NULL)153		return -ENOMEM;154 155	err = get_meter(bebob, buf, size);156	if (err < 0)157		goto end;158 159	/* if synced, this value is the same as SFC of FDF in CIP header */160	*sync = (buf[size - 2] != 0xff);161end:162	kfree(buf);163	return err;164}165 166/*167 * dig_fmt: 0x00:S/PDIF, 0x01:ADAT168 * clk_lock: 0x00:unlock, 0x01:lock169 */170static int171avc_maudio_set_special_clk(struct snd_bebob *bebob, unsigned int clk_src,172			   unsigned int dig_in_fmt, unsigned int dig_out_fmt,173			   unsigned int clk_lock)174{175	struct special_params *params = bebob->maudio_special_quirk;176	int err;177	u8 *buf;178 179	if (amdtp_stream_running(&bebob->rx_stream) ||180	    amdtp_stream_running(&bebob->tx_stream))181		return -EBUSY;182 183	buf = kmalloc(12, GFP_KERNEL);184	if (buf == NULL)185		return -ENOMEM;186 187	buf[0]  = 0x00;		/* CONTROL */188	buf[1]  = 0xff;		/* UNIT */189	buf[2]  = 0x00;		/* vendor dependent */190	buf[3]  = 0x04;		/* company ID high */191	buf[4]  = 0x00;		/* company ID middle */192	buf[5]  = 0x04;		/* company ID low */193	buf[6]  = 0xff & clk_src;	/* clock source */194	buf[7]  = 0xff & dig_in_fmt;	/* input digital format */195	buf[8]  = 0xff & dig_out_fmt;	/* output digital format */196	buf[9]  = 0xff & clk_lock;	/* lock these settings */197	buf[10] = 0x00;		/* padding  */198	buf[11] = 0x00;		/* padding */199 200	err = fcp_avc_transaction(bebob->unit, buf, 12, buf, 12,201				  BIT(1) | BIT(2) | BIT(3) | BIT(4) |202				  BIT(5) | BIT(6) | BIT(7) | BIT(8) |203				  BIT(9));204	if ((err > 0) && (err < 10))205		err = -EIO;206	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */207		err = -ENOSYS;208	else if (buf[0] == 0x0a) /* REJECTED */209		err = -EINVAL;210	if (err < 0)211		goto end;212 213	params->clk_src		= buf[6];214	params->dig_in_fmt	= buf[7];215	params->dig_out_fmt	= buf[8];216	params->clk_lock	= buf[9];217 218	if (params->ctl_id_sync)219		snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,220			       params->ctl_id_sync);221 222	err = 0;223end:224	kfree(buf);225	return err;226}227static void228special_stream_formation_set(struct snd_bebob *bebob)229{230	static const unsigned int ch_table[2][2][3] = {231		/* AMDTP_OUT_STREAM */232		{ {  6,  6,  4 },	/* SPDIF */233		  { 12,  8,  4 } },	/* ADAT */234		/* AMDTP_IN_STREAM */235		{ { 10, 10,  2 },	/* SPDIF */236		  { 16, 12,  2 } }	/* ADAT */237	};238	struct special_params *params = bebob->maudio_special_quirk;239	unsigned int i, max;240 241	max = SND_BEBOB_STRM_FMT_ENTRIES - 1;242	if (!params->is1814)243		max -= 2;244 245	for (i = 0; i < max; i++) {246		bebob->tx_stream_formations[i + 1].pcm =247			ch_table[AMDTP_IN_STREAM][params->dig_in_fmt][i / 2];248		bebob->tx_stream_formations[i + 1].midi = 1;249 250		bebob->rx_stream_formations[i + 1].pcm =251			ch_table[AMDTP_OUT_STREAM][params->dig_out_fmt][i / 2];252		bebob->rx_stream_formations[i + 1].midi = 1;253	}254}255 256static int add_special_controls(struct snd_bebob *bebob);257int258snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814)259{260	struct special_params *params;261	int err;262 263	params = devm_kzalloc(&bebob->card->card_dev,264			      sizeof(struct special_params), GFP_KERNEL);265	if (!params)266		return -ENOMEM;267 268	mutex_lock(&bebob->mutex);269 270	bebob->maudio_special_quirk = (void *)params;271	params->is1814 = is1814;272 273	/* initialize these parameters because driver is not allowed to ask */274	bebob->rx_stream.context = ERR_PTR(-1);275	bebob->tx_stream.context = ERR_PTR(-1);276	err = avc_maudio_set_special_clk(bebob, 0x03, 0x00, 0x00, 0x00);277	if (err < 0) {278		dev_err(&bebob->unit->device,279			"fail to initialize clock params: %d\n", err);280		goto end;281	}282 283	err = add_special_controls(bebob);284	if (err < 0)285		goto end;286 287	special_stream_formation_set(bebob);288 289	if (params->is1814) {290		bebob->midi_input_ports = 1;291		bebob->midi_output_ports = 1;292	} else {293		bebob->midi_input_ports = 2;294		bebob->midi_output_ports = 2;295	}296end:297	mutex_unlock(&bebob->mutex);298	return err;299}300 301/* Input plug shows actual rate. Output plug is needless for this purpose. */302static int special_get_rate(struct snd_bebob *bebob, unsigned int *rate)303{304	int err, trials;305 306	trials = 0;307	do {308		err = avc_general_get_sig_fmt(bebob->unit, rate,309					      AVC_GENERAL_PLUG_DIR_IN, 0);310	} while (err == -EAGAIN && ++trials < 3);311 312	return err;313}314static int special_set_rate(struct snd_bebob *bebob, unsigned int rate)315{316	struct special_params *params = bebob->maudio_special_quirk;317	int err;318 319	err = avc_general_set_sig_fmt(bebob->unit, rate,320				      AVC_GENERAL_PLUG_DIR_OUT, 0);321	if (err < 0)322		goto end;323 324	/*325	 * Just after changing sampling rate for output, a followed command326	 * for input is easy to fail. This is a workaround fot this issue.327	 */328	msleep(100);329 330	err = avc_general_set_sig_fmt(bebob->unit, rate,331				      AVC_GENERAL_PLUG_DIR_IN, 0);332	if (err < 0)333		goto end;334 335	if (params->ctl_id_sync)336		snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,337			       params->ctl_id_sync);338end:339	return err;340}341 342/* Clock source control for special firmware */343static const enum snd_bebob_clock_type special_clk_types[] = {344	SND_BEBOB_CLOCK_TYPE_INTERNAL,	/* With digital mute */345	SND_BEBOB_CLOCK_TYPE_EXTERNAL,	/* SPDIF/ADAT */346	SND_BEBOB_CLOCK_TYPE_EXTERNAL,	/* Word Clock */347	SND_BEBOB_CLOCK_TYPE_INTERNAL,348};349static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)350{351	struct special_params *params = bebob->maudio_special_quirk;352	*id = params->clk_src;353	return 0;354}355static int special_clk_ctl_info(struct snd_kcontrol *kctl,356				struct snd_ctl_elem_info *einf)357{358	static const char *const special_clk_labels[] = {359		"Internal with Digital Mute",360		"Digital",361		"Word Clock",362		"Internal"363	};364	return snd_ctl_enum_info(einf, 1, ARRAY_SIZE(special_clk_types),365				 special_clk_labels);366}367static int special_clk_ctl_get(struct snd_kcontrol *kctl,368			       struct snd_ctl_elem_value *uval)369{370	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);371	struct special_params *params = bebob->maudio_special_quirk;372	uval->value.enumerated.item[0] = params->clk_src;373	return 0;374}375static int special_clk_ctl_put(struct snd_kcontrol *kctl,376			       struct snd_ctl_elem_value *uval)377{378	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);379	struct special_params *params = bebob->maudio_special_quirk;380	int err, id;381 382	id = uval->value.enumerated.item[0];383	if (id >= ARRAY_SIZE(special_clk_types))384		return -EINVAL;385 386	mutex_lock(&bebob->mutex);387 388	err = avc_maudio_set_special_clk(bebob, id,389					 params->dig_in_fmt,390					 params->dig_out_fmt,391					 params->clk_lock);392	mutex_unlock(&bebob->mutex);393 394	if (err >= 0)395		err = 1;396 397	return err;398}399static const struct snd_kcontrol_new special_clk_ctl = {400	.name	= "Clock Source",401	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,402	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,403	.info	= special_clk_ctl_info,404	.get	= special_clk_ctl_get,405	.put	= special_clk_ctl_put406};407 408/* Clock synchronization control for special firmware */409static int special_sync_ctl_info(struct snd_kcontrol *kctl,410				 struct snd_ctl_elem_info *einf)411{412	einf->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;413	einf->count = 1;414	einf->value.integer.min = 0;415	einf->value.integer.max = 1;416 417	return 0;418}419static int special_sync_ctl_get(struct snd_kcontrol *kctl,420				struct snd_ctl_elem_value *uval)421{422	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);423	int err;424	bool synced = 0;425 426	err = check_clk_sync(bebob, METER_SIZE_SPECIAL, &synced);427	if (err >= 0)428		uval->value.integer.value[0] = synced;429 430	return 0;431}432static const struct snd_kcontrol_new special_sync_ctl = {433	.name	= "Sync Status",434	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,435	.access	= SNDRV_CTL_ELEM_ACCESS_READ,436	.info	= special_sync_ctl_info,437	.get	= special_sync_ctl_get,438};439 440/* Digital input interface control for special firmware */441static const char *const special_dig_in_iface_labels[] = {442	"S/PDIF Optical", "S/PDIF Coaxial", "ADAT Optical"443};444static int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl,445					 struct snd_ctl_elem_info *einf)446{447	return snd_ctl_enum_info(einf, 1,448				 ARRAY_SIZE(special_dig_in_iface_labels),449				 special_dig_in_iface_labels);450}451static int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl,452					struct snd_ctl_elem_value *uval)453{454	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);455	struct special_params *params = bebob->maudio_special_quirk;456	unsigned int dig_in_iface;457	int err, val;458 459	mutex_lock(&bebob->mutex);460 461	err = avc_audio_get_selector(bebob->unit, 0x00, 0x04,462				     &dig_in_iface);463	if (err < 0) {464		dev_err(&bebob->unit->device,465			"fail to get digital input interface: %d\n", err);466		goto end;467	}468 469	/* encoded id for user value */470	val = (params->dig_in_fmt << 1) | (dig_in_iface & 0x01);471 472	/* for ADAT Optical */473	if (val > 2)474		val = 2;475 476	uval->value.enumerated.item[0] = val;477end:478	mutex_unlock(&bebob->mutex);479	return err;480}481static int special_dig_in_iface_ctl_set(struct snd_kcontrol *kctl,482					struct snd_ctl_elem_value *uval)483{484	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);485	struct special_params *params = bebob->maudio_special_quirk;486	unsigned int id, dig_in_fmt, dig_in_iface;487	int err;488 489	id = uval->value.enumerated.item[0];490	if (id >= ARRAY_SIZE(special_dig_in_iface_labels))491		return -EINVAL;492 493	/* decode user value */494	dig_in_fmt = (id >> 1) & 0x01;495	dig_in_iface = id & 0x01;496 497	mutex_lock(&bebob->mutex);498 499	err = avc_maudio_set_special_clk(bebob,500					 params->clk_src,501					 dig_in_fmt,502					 params->dig_out_fmt,503					 params->clk_lock);504	if (err < 0)505		goto end;506 507	/* For ADAT, optical interface is only available. */508	if (params->dig_in_fmt > 0) {509		err = 1;510		goto end;511	}512 513	/* For S/PDIF, optical/coaxial interfaces are selectable. */514	err = avc_audio_set_selector(bebob->unit, 0x00, 0x04, dig_in_iface);515	if (err < 0)516		dev_err(&bebob->unit->device,517			"fail to set digital input interface: %d\n", err);518	err = 1;519end:520	special_stream_formation_set(bebob);521	mutex_unlock(&bebob->mutex);522	return err;523}524static const struct snd_kcontrol_new special_dig_in_iface_ctl = {525	.name	= "Digital Input Interface",526	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,527	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,528	.info	= special_dig_in_iface_ctl_info,529	.get	= special_dig_in_iface_ctl_get,530	.put	= special_dig_in_iface_ctl_set531};532 533/* Digital output interface control for special firmware */534static const char *const special_dig_out_iface_labels[] = {535	"S/PDIF Optical and Coaxial", "ADAT Optical"536};537static int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl,538					  struct snd_ctl_elem_info *einf)539{540	return snd_ctl_enum_info(einf, 1,541				 ARRAY_SIZE(special_dig_out_iface_labels),542				 special_dig_out_iface_labels);543}544static int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl,545					 struct snd_ctl_elem_value *uval)546{547	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);548	struct special_params *params = bebob->maudio_special_quirk;549	mutex_lock(&bebob->mutex);550	uval->value.enumerated.item[0] = params->dig_out_fmt;551	mutex_unlock(&bebob->mutex);552	return 0;553}554static int special_dig_out_iface_ctl_set(struct snd_kcontrol *kctl,555					 struct snd_ctl_elem_value *uval)556{557	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);558	struct special_params *params = bebob->maudio_special_quirk;559	unsigned int id;560	int err;561 562	id = uval->value.enumerated.item[0];563	if (id >= ARRAY_SIZE(special_dig_out_iface_labels))564		return -EINVAL;565 566	mutex_lock(&bebob->mutex);567 568	err = avc_maudio_set_special_clk(bebob,569					 params->clk_src,570					 params->dig_in_fmt,571					 id, params->clk_lock);572	if (err >= 0) {573		special_stream_formation_set(bebob);574		err = 1;575	}576 577	mutex_unlock(&bebob->mutex);578	return err;579}580static const struct snd_kcontrol_new special_dig_out_iface_ctl = {581	.name	= "Digital Output Interface",582	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,583	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,584	.info	= special_dig_out_iface_ctl_info,585	.get	= special_dig_out_iface_ctl_get,586	.put	= special_dig_out_iface_ctl_set587};588 589static int add_special_controls(struct snd_bebob *bebob)590{591	struct snd_kcontrol *kctl;592	struct special_params *params = bebob->maudio_special_quirk;593	int err;594 595	kctl = snd_ctl_new1(&special_clk_ctl, bebob);596	err = snd_ctl_add(bebob->card, kctl);597	if (err < 0)598		goto end;599 600	kctl = snd_ctl_new1(&special_sync_ctl, bebob);601	err = snd_ctl_add(bebob->card, kctl);602	if (err < 0)603		goto end;604	params->ctl_id_sync = &kctl->id;605 606	kctl = snd_ctl_new1(&special_dig_in_iface_ctl, bebob);607	err = snd_ctl_add(bebob->card, kctl);608	if (err < 0)609		goto end;610 611	kctl = snd_ctl_new1(&special_dig_out_iface_ctl, bebob);612	err = snd_ctl_add(bebob->card, kctl);613end:614	return err;615}616 617/* Hardware metering for special firmware */618static const char *const special_meter_labels[] = {619	ANA_IN, ANA_IN, ANA_IN, ANA_IN,620	SPDIF_IN,621	ADAT_IN, ADAT_IN, ADAT_IN, ADAT_IN,622	ANA_OUT, ANA_OUT,623	SPDIF_OUT,624	ADAT_OUT, ADAT_OUT, ADAT_OUT, ADAT_OUT,625	HP_OUT, HP_OUT,626	AUX_OUT627};628static int629special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)630{631	__be16 *buf;632	unsigned int i, c, channels;633	int err;634 635	channels = ARRAY_SIZE(special_meter_labels) * 2;636	if (size < channels * sizeof(u32))637		return -EINVAL;638 639	/* omit last 4 bytes because it's clock info. */640	buf = kmalloc(METER_SIZE_SPECIAL - 4, GFP_KERNEL);641	if (buf == NULL)642		return -ENOMEM;643 644	err = get_meter(bebob, (void *)buf, METER_SIZE_SPECIAL - 4);645	if (err < 0)646		goto end;647 648	/* Its format is u16 and some channels are unknown. */649	i = 0;650	for (c = 2; c < channels + 2; c++)651		target[i++] = be16_to_cpu(buf[c]) << 16;652end:653	kfree(buf);654	return err;655}656 657/* last 4 bytes are omitted because it's clock info. */658static const char *const fw410_meter_labels[] = {659	ANA_IN, DIG_IN,660	ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT,661	HP_OUT662};663static const char *const audiophile_meter_labels[] = {664	ANA_IN, DIG_IN,665	ANA_OUT, ANA_OUT, DIG_OUT,666	HP_OUT, AUX_OUT,667};668static const char *const solo_meter_labels[] = {669	ANA_IN, DIG_IN,670	STRM_IN, STRM_IN,671	ANA_OUT, DIG_OUT672};673 674/* no clock info */675static const char *const ozonic_meter_labels[] = {676	ANA_IN, ANA_IN,677	STRM_IN, STRM_IN,678	ANA_OUT, ANA_OUT679};680/* TODO: need testers. these positions are based on authour's assumption */681static const char *const nrv10_meter_labels[] = {682	ANA_IN, ANA_IN, ANA_IN, ANA_IN,683	DIG_IN,684	ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,685	DIG_IN686};687static int688normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)689{690	const struct snd_bebob_meter_spec *spec = bebob->spec->meter;691	unsigned int c, channels;692	int err;693 694	channels = spec->num * 2;695	if (size < channels * sizeof(u32))696		return -EINVAL;697 698	err = get_meter(bebob, (void *)buf, size);699	if (err < 0)700		goto end;701 702	for (c = 0; c < channels; c++)703		be32_to_cpus(&buf[c]);704 705	/* swap stream channels because inverted */706	if (spec->labels == solo_meter_labels) {707		swap(buf[4], buf[6]);708		swap(buf[5], buf[7]);709	}710end:711	return err;712}713 714/* for special customized devices */715static const struct snd_bebob_rate_spec special_rate_spec = {716	.get	= &special_get_rate,717	.set	= &special_set_rate,718};719static const struct snd_bebob_clock_spec special_clk_spec = {720	.num	= ARRAY_SIZE(special_clk_types),721	.types	= special_clk_types,722	.get	= &special_clk_get,723};724static const struct snd_bebob_meter_spec special_meter_spec = {725	.num	= ARRAY_SIZE(special_meter_labels),726	.labels	= special_meter_labels,727	.get	= &special_meter_get728};729const struct snd_bebob_spec maudio_special_spec = {730	.clock	= &special_clk_spec,731	.rate	= &special_rate_spec,732	.meter	= &special_meter_spec733};734 735/* Firewire 410 specification */736static const struct snd_bebob_rate_spec usual_rate_spec = {737	.get	= &snd_bebob_stream_get_rate,738	.set	= &snd_bebob_stream_set_rate,739};740static const struct snd_bebob_meter_spec fw410_meter_spec = {741	.num	= ARRAY_SIZE(fw410_meter_labels),742	.labels	= fw410_meter_labels,743	.get	= &normal_meter_get744};745const struct snd_bebob_spec maudio_fw410_spec = {746	.clock	= NULL,747	.rate	= &usual_rate_spec,748	.meter	= &fw410_meter_spec749};750 751/* Firewire Audiophile specification */752static const struct snd_bebob_meter_spec audiophile_meter_spec = {753	.num	= ARRAY_SIZE(audiophile_meter_labels),754	.labels	= audiophile_meter_labels,755	.get	= &normal_meter_get756};757const struct snd_bebob_spec maudio_audiophile_spec = {758	.clock	= NULL,759	.rate	= &usual_rate_spec,760	.meter	= &audiophile_meter_spec761};762 763/* Firewire Solo specification */764static const struct snd_bebob_meter_spec solo_meter_spec = {765	.num	= ARRAY_SIZE(solo_meter_labels),766	.labels	= solo_meter_labels,767	.get	= &normal_meter_get768};769const struct snd_bebob_spec maudio_solo_spec = {770	.clock	= NULL,771	.rate	= &usual_rate_spec,772	.meter	= &solo_meter_spec773};774 775/* Ozonic specification */776static const struct snd_bebob_meter_spec ozonic_meter_spec = {777	.num	= ARRAY_SIZE(ozonic_meter_labels),778	.labels	= ozonic_meter_labels,779	.get	= &normal_meter_get780};781const struct snd_bebob_spec maudio_ozonic_spec = {782	.clock	= NULL,783	.rate	= &usual_rate_spec,784	.meter	= &ozonic_meter_spec785};786 787/* NRV10 specification */788static const struct snd_bebob_meter_spec nrv10_meter_spec = {789	.num	= ARRAY_SIZE(nrv10_meter_labels),790	.labels	= nrv10_meter_labels,791	.get	= &normal_meter_get792};793const struct snd_bebob_spec maudio_nrv10_spec = {794	.clock	= NULL,795	.rate	= &usual_rate_spec,796	.meter	= &nrv10_meter_spec797};798