brintos

brintos / linux-shallow public Read only

0
0
Text · 15.9 KiB · 9bd8dcb Raw
618 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Linux driver for TerraTec DMX 6Fire USB4 *5 * Mixer control6 *7 * Author:	Torsten Schenk <torsten.schenk@zoho.com>8 * Created:	Jan 01, 20119 * Copyright:	(C) Torsten Schenk10 *11 * Thanks to:12 * - Holger Ruckdeschel: he found out how to control individual channel13 *   volumes and introduced mute switch14 */15 16#include <linux/interrupt.h>17#include <sound/control.h>18#include <sound/tlv.h>19 20#include "control.h"21#include "comm.h"22#include "chip.h"23 24static const char * const opt_coax_texts[2] = { "Optical", "Coax" };25static const char * const line_phono_texts[2] = { "Line", "Phono" };26 27/*28 * data that needs to be sent to device. sets up card internal stuff.29 * values dumped from windows driver and filtered by trial'n'error.30 */31static const struct {32	u8 type;33	u8 reg;34	u8 value;35}36init_data[] = {37	{ 0x22, 0x00, 0x00 }, { 0x20, 0x00, 0x08 }, { 0x22, 0x01, 0x01 },38	{ 0x20, 0x01, 0x08 }, { 0x22, 0x02, 0x00 }, { 0x20, 0x02, 0x08 },39	{ 0x22, 0x03, 0x00 }, { 0x20, 0x03, 0x08 }, { 0x22, 0x04, 0x00 },40	{ 0x20, 0x04, 0x08 }, { 0x22, 0x05, 0x01 }, { 0x20, 0x05, 0x08 },41	{ 0x22, 0x04, 0x01 }, { 0x12, 0x04, 0x00 }, { 0x12, 0x05, 0x00 },42	{ 0x12, 0x0d, 0x38 }, { 0x12, 0x21, 0x82 }, { 0x12, 0x22, 0x80 },43	{ 0x12, 0x23, 0x00 }, { 0x12, 0x06, 0x02 }, { 0x12, 0x03, 0x00 },44	{ 0x12, 0x02, 0x00 }, { 0x22, 0x03, 0x01 },45	{ 0 } /* TERMINATING ENTRY */46};47 48static const int rates_altsetting[] = { 1, 1, 2, 2, 3, 3 };49/* values to write to soundcard register for all samplerates */50static const u16 rates_6fire_vl[] = {0x00, 0x01, 0x00, 0x01, 0x00, 0x01};51static const u16 rates_6fire_vh[] = {0x11, 0x11, 0x10, 0x10, 0x00, 0x00};52 53static DECLARE_TLV_DB_MINMAX(tlv_output, -9000, 0);54static DECLARE_TLV_DB_MINMAX(tlv_input, -1500, 1500);55 56enum {57	DIGITAL_THRU_ONLY_SAMPLERATE = 358};59 60static void usb6fire_control_output_vol_update(struct control_runtime *rt)61{62	struct comm_runtime *comm_rt = rt->chip->comm;63	int i;64 65	if (comm_rt)66		for (i = 0; i < 6; i++)67			if (!(rt->ovol_updated & (1 << i))) {68				comm_rt->write8(comm_rt, 0x12, 0x0f + i,69					180 - rt->output_vol[i]);70				rt->ovol_updated |= 1 << i;71			}72}73 74static void usb6fire_control_output_mute_update(struct control_runtime *rt)75{76	struct comm_runtime *comm_rt = rt->chip->comm;77 78	if (comm_rt)79		comm_rt->write8(comm_rt, 0x12, 0x0e, ~rt->output_mute);80}81 82static void usb6fire_control_input_vol_update(struct control_runtime *rt)83{84	struct comm_runtime *comm_rt = rt->chip->comm;85	int i;86 87	if (comm_rt)88		for (i = 0; i < 2; i++)89			if (!(rt->ivol_updated & (1 << i))) {90				comm_rt->write8(comm_rt, 0x12, 0x1c + i,91					rt->input_vol[i] & 0x3f);92				rt->ivol_updated |= 1 << i;93			}94}95 96static void usb6fire_control_line_phono_update(struct control_runtime *rt)97{98	struct comm_runtime *comm_rt = rt->chip->comm;99	if (comm_rt) {100		comm_rt->write8(comm_rt, 0x22, 0x02, rt->line_phono_switch);101		comm_rt->write8(comm_rt, 0x21, 0x02, rt->line_phono_switch);102	}103}104 105static void usb6fire_control_opt_coax_update(struct control_runtime *rt)106{107	struct comm_runtime *comm_rt = rt->chip->comm;108	if (comm_rt) {109		comm_rt->write8(comm_rt, 0x22, 0x00, rt->opt_coax_switch);110		comm_rt->write8(comm_rt, 0x21, 0x00, rt->opt_coax_switch);111	}112}113 114static int usb6fire_control_set_rate(struct control_runtime *rt, int rate)115{116	int ret;117	struct usb_device *device = rt->chip->dev;118	struct comm_runtime *comm_rt = rt->chip->comm;119 120	if (rate < 0 || rate >= CONTROL_N_RATES)121		return -EINVAL;122 123	ret = usb_set_interface(device, 1, rates_altsetting[rate]);124	if (ret < 0)125		return ret;126 127	/* set soundcard clock */128	ret = comm_rt->write16(comm_rt, 0x02, 0x01, rates_6fire_vl[rate],129			rates_6fire_vh[rate]);130	if (ret < 0)131		return ret;132 133	return 0;134}135 136static int usb6fire_control_set_channels(137	struct control_runtime *rt, int n_analog_out,138	int n_analog_in, bool spdif_out, bool spdif_in)139{140	int ret;141	struct comm_runtime *comm_rt = rt->chip->comm;142 143	/* enable analog inputs and outputs144	 * (one bit per stereo-channel) */145	ret = comm_rt->write16(comm_rt, 0x02, 0x02,146			(1 << (n_analog_out / 2)) - 1,147			(1 << (n_analog_in / 2)) - 1);148	if (ret < 0)149		return ret;150 151	/* disable digital inputs and outputs */152	/* TODO: use spdif_x to enable/disable digital channels */153	ret = comm_rt->write16(comm_rt, 0x02, 0x03, 0x00, 0x00);154	if (ret < 0)155		return ret;156 157	return 0;158}159 160static int usb6fire_control_streaming_update(struct control_runtime *rt)161{162	struct comm_runtime *comm_rt = rt->chip->comm;163 164	if (comm_rt) {165		if (!rt->usb_streaming && rt->digital_thru_switch)166			usb6fire_control_set_rate(rt,167				DIGITAL_THRU_ONLY_SAMPLERATE);168		return comm_rt->write16(comm_rt, 0x02, 0x00, 0x00,169			(rt->usb_streaming ? 0x01 : 0x00) |170			(rt->digital_thru_switch ? 0x08 : 0x00));171	}172	return -EINVAL;173}174 175static int usb6fire_control_output_vol_info(struct snd_kcontrol *kcontrol,176		struct snd_ctl_elem_info *uinfo)177{178	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;179	uinfo->count = 2;180	uinfo->value.integer.min = 0;181	uinfo->value.integer.max = 180;182	return 0;183}184 185static int usb6fire_control_output_vol_put(struct snd_kcontrol *kcontrol,186		struct snd_ctl_elem_value *ucontrol)187{188	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);189	unsigned int ch = kcontrol->private_value;190	int changed = 0;191 192	if (ch > 4) {193		dev_err(&rt->chip->dev->dev,194			"Invalid channel in volume control.");195		return -EINVAL;196	}197 198	if (rt->output_vol[ch] != ucontrol->value.integer.value[0]) {199		rt->output_vol[ch] = ucontrol->value.integer.value[0];200		rt->ovol_updated &= ~(1 << ch);201		changed = 1;202	}203	if (rt->output_vol[ch + 1] != ucontrol->value.integer.value[1]) {204		rt->output_vol[ch + 1] = ucontrol->value.integer.value[1];205		rt->ovol_updated &= ~(2 << ch);206		changed = 1;207	}208 209	if (changed)210		usb6fire_control_output_vol_update(rt);211 212	return changed;213}214 215static int usb6fire_control_output_vol_get(struct snd_kcontrol *kcontrol,216		struct snd_ctl_elem_value *ucontrol)217{218	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);219	unsigned int ch = kcontrol->private_value;220 221	if (ch > 4) {222		dev_err(&rt->chip->dev->dev,223			"Invalid channel in volume control.");224		return -EINVAL;225	}226 227	ucontrol->value.integer.value[0] = rt->output_vol[ch];228	ucontrol->value.integer.value[1] = rt->output_vol[ch + 1];229	return 0;230}231 232static int usb6fire_control_output_mute_put(struct snd_kcontrol *kcontrol,233	struct snd_ctl_elem_value *ucontrol)234{235	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);236	unsigned int ch = kcontrol->private_value;237	u8 old = rt->output_mute;238	u8 value = 0;239 240	if (ch > 4) {241		dev_err(&rt->chip->dev->dev,242			"Invalid channel in volume control.");243		return -EINVAL;244	}245 246	rt->output_mute &= ~(3 << ch);247	if (ucontrol->value.integer.value[0])248		value |= 1;249	if (ucontrol->value.integer.value[1])250		value |= 2;251	rt->output_mute |= value << ch;252 253	if (rt->output_mute != old)254		usb6fire_control_output_mute_update(rt);255 256	return rt->output_mute != old;257}258 259static int usb6fire_control_output_mute_get(struct snd_kcontrol *kcontrol,260	struct snd_ctl_elem_value *ucontrol)261{262	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);263	unsigned int ch = kcontrol->private_value;264	u8 value = rt->output_mute >> ch;265 266	if (ch > 4) {267		dev_err(&rt->chip->dev->dev,268			"Invalid channel in volume control.");269		return -EINVAL;270	}271 272	ucontrol->value.integer.value[0] = 1 & value;273	value >>= 1;274	ucontrol->value.integer.value[1] = 1 & value;275 276	return 0;277}278 279static int usb6fire_control_input_vol_info(struct snd_kcontrol *kcontrol,280		struct snd_ctl_elem_info *uinfo)281{282	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;283	uinfo->count = 2;284	uinfo->value.integer.min = 0;285	uinfo->value.integer.max = 30;286	return 0;287}288 289static int usb6fire_control_input_vol_put(struct snd_kcontrol *kcontrol,290		struct snd_ctl_elem_value *ucontrol)291{292	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);293	int changed = 0;294 295	if (rt->input_vol[0] != ucontrol->value.integer.value[0]) {296		rt->input_vol[0] = ucontrol->value.integer.value[0] - 15;297		rt->ivol_updated &= ~(1 << 0);298		changed = 1;299	}300	if (rt->input_vol[1] != ucontrol->value.integer.value[1]) {301		rt->input_vol[1] = ucontrol->value.integer.value[1] - 15;302		rt->ivol_updated &= ~(1 << 1);303		changed = 1;304	}305 306	if (changed)307		usb6fire_control_input_vol_update(rt);308 309	return changed;310}311 312static int usb6fire_control_input_vol_get(struct snd_kcontrol *kcontrol,313		struct snd_ctl_elem_value *ucontrol)314{315	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);316 317	ucontrol->value.integer.value[0] = rt->input_vol[0] + 15;318	ucontrol->value.integer.value[1] = rt->input_vol[1] + 15;319 320	return 0;321}322 323static int usb6fire_control_line_phono_info(struct snd_kcontrol *kcontrol,324		struct snd_ctl_elem_info *uinfo)325{326	return snd_ctl_enum_info(uinfo, 1, 2, line_phono_texts);327}328 329static int usb6fire_control_line_phono_put(struct snd_kcontrol *kcontrol,330		struct snd_ctl_elem_value *ucontrol)331{332	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);333	int changed = 0;334	if (rt->line_phono_switch != ucontrol->value.integer.value[0]) {335		rt->line_phono_switch = ucontrol->value.integer.value[0];336		usb6fire_control_line_phono_update(rt);337		changed = 1;338	}339	return changed;340}341 342static int usb6fire_control_line_phono_get(struct snd_kcontrol *kcontrol,343		struct snd_ctl_elem_value *ucontrol)344{345	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);346	ucontrol->value.integer.value[0] = rt->line_phono_switch;347	return 0;348}349 350static int usb6fire_control_opt_coax_info(struct snd_kcontrol *kcontrol,351		struct snd_ctl_elem_info *uinfo)352{353	return snd_ctl_enum_info(uinfo, 1, 2, opt_coax_texts);354}355 356static int usb6fire_control_opt_coax_put(struct snd_kcontrol *kcontrol,357		struct snd_ctl_elem_value *ucontrol)358{359	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);360	int changed = 0;361 362	if (rt->opt_coax_switch != ucontrol->value.enumerated.item[0]) {363		rt->opt_coax_switch = ucontrol->value.enumerated.item[0];364		usb6fire_control_opt_coax_update(rt);365		changed = 1;366	}367	return changed;368}369 370static int usb6fire_control_opt_coax_get(struct snd_kcontrol *kcontrol,371		struct snd_ctl_elem_value *ucontrol)372{373	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);374	ucontrol->value.enumerated.item[0] = rt->opt_coax_switch;375	return 0;376}377 378static int usb6fire_control_digital_thru_put(struct snd_kcontrol *kcontrol,379		struct snd_ctl_elem_value *ucontrol)380{381	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);382	int changed = 0;383 384	if (rt->digital_thru_switch != ucontrol->value.integer.value[0]) {385		rt->digital_thru_switch = ucontrol->value.integer.value[0];386		usb6fire_control_streaming_update(rt);387		changed = 1;388	}389	return changed;390}391 392static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol,393		struct snd_ctl_elem_value *ucontrol)394{395	struct control_runtime *rt = snd_kcontrol_chip(kcontrol);396	ucontrol->value.integer.value[0] = rt->digital_thru_switch;397	return 0;398}399 400static const struct snd_kcontrol_new vol_elements[] = {401	{402		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,403		.name = "Analog Playback Volume",404		.index = 0,405		.private_value = 0,406		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |407			SNDRV_CTL_ELEM_ACCESS_TLV_READ,408		.info = usb6fire_control_output_vol_info,409		.get = usb6fire_control_output_vol_get,410		.put = usb6fire_control_output_vol_put,411		.tlv = { .p = tlv_output }412	},413	{414		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,415		.name = "Analog Playback Volume",416		.index = 1,417		.private_value = 2,418		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |419			SNDRV_CTL_ELEM_ACCESS_TLV_READ,420		.info = usb6fire_control_output_vol_info,421		.get = usb6fire_control_output_vol_get,422		.put = usb6fire_control_output_vol_put,423		.tlv = { .p = tlv_output }424	},425	{426		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,427		.name = "Analog Playback Volume",428		.index = 2,429		.private_value = 4,430		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |431			SNDRV_CTL_ELEM_ACCESS_TLV_READ,432		.info = usb6fire_control_output_vol_info,433		.get = usb6fire_control_output_vol_get,434		.put = usb6fire_control_output_vol_put,435		.tlv = { .p = tlv_output }436	},437	{}438};439 440static const struct snd_kcontrol_new mute_elements[] = {441	{442		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,443		.name = "Analog Playback Switch",444		.index = 0,445		.private_value = 0,446		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,447		.info = snd_ctl_boolean_stereo_info,448		.get = usb6fire_control_output_mute_get,449		.put = usb6fire_control_output_mute_put,450	},451	{452		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,453		.name = "Analog Playback Switch",454		.index = 1,455		.private_value = 2,456		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,457		.info = snd_ctl_boolean_stereo_info,458		.get = usb6fire_control_output_mute_get,459		.put = usb6fire_control_output_mute_put,460	},461	{462		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,463		.name = "Analog Playback Switch",464		.index = 2,465		.private_value = 4,466		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,467		.info = snd_ctl_boolean_stereo_info,468		.get = usb6fire_control_output_mute_get,469		.put = usb6fire_control_output_mute_put,470	},471	{}472};473 474static const struct snd_kcontrol_new elements[] = {475	{476		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,477		.name = "Line/Phono Capture Route",478		.index = 0,479		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,480		.info = usb6fire_control_line_phono_info,481		.get = usb6fire_control_line_phono_get,482		.put = usb6fire_control_line_phono_put483	},484	{485		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,486		.name = "Opt/Coax Capture Route",487		.index = 0,488		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,489		.info = usb6fire_control_opt_coax_info,490		.get = usb6fire_control_opt_coax_get,491		.put = usb6fire_control_opt_coax_put492	},493	{494		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,495		.name = "Digital Thru Playback Route",496		.index = 0,497		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,498		.info = snd_ctl_boolean_mono_info,499		.get = usb6fire_control_digital_thru_get,500		.put = usb6fire_control_digital_thru_put501	},502	{503		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,504		.name = "Analog Capture Volume",505		.index = 0,506		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |507			SNDRV_CTL_ELEM_ACCESS_TLV_READ,508		.info = usb6fire_control_input_vol_info,509		.get = usb6fire_control_input_vol_get,510		.put = usb6fire_control_input_vol_put,511		.tlv = { .p = tlv_input }512	},513	{}514};515 516static int usb6fire_control_add_virtual(517	struct control_runtime *rt,518	struct snd_card *card,519	char *name,520	const struct snd_kcontrol_new *elems)521{522	int ret;523	int i;524	struct snd_kcontrol *vmaster =525		snd_ctl_make_virtual_master(name, tlv_output);526	struct snd_kcontrol *control;527 528	if (!vmaster)529		return -ENOMEM;530	ret = snd_ctl_add(card, vmaster);531	if (ret < 0)532		return ret;533 534	i = 0;535	while (elems[i].name) {536		control = snd_ctl_new1(&elems[i], rt);537		if (!control)538			return -ENOMEM;539		ret = snd_ctl_add(card, control);540		if (ret < 0)541			return ret;542		ret = snd_ctl_add_follower(vmaster, control);543		if (ret < 0)544			return ret;545		i++;546	}547	return 0;548}549 550int usb6fire_control_init(struct sfire_chip *chip)551{552	int i;553	int ret;554	struct control_runtime *rt = kzalloc(sizeof(struct control_runtime),555			GFP_KERNEL);556	struct comm_runtime *comm_rt = chip->comm;557 558	if (!rt)559		return -ENOMEM;560 561	rt->chip = chip;562	rt->update_streaming = usb6fire_control_streaming_update;563	rt->set_rate = usb6fire_control_set_rate;564	rt->set_channels = usb6fire_control_set_channels;565 566	i = 0;567	while (init_data[i].type) {568		comm_rt->write8(comm_rt, init_data[i].type, init_data[i].reg,569				init_data[i].value);570		i++;571	}572 573	usb6fire_control_opt_coax_update(rt);574	usb6fire_control_line_phono_update(rt);575	usb6fire_control_output_vol_update(rt);576	usb6fire_control_output_mute_update(rt);577	usb6fire_control_input_vol_update(rt);578	usb6fire_control_streaming_update(rt);579 580	ret = usb6fire_control_add_virtual(rt, chip->card,581		"Master Playback Volume", vol_elements);582	if (ret) {583		dev_err(&chip->dev->dev, "cannot add control.\n");584		kfree(rt);585		return ret;586	}587	ret = usb6fire_control_add_virtual(rt, chip->card,588		"Master Playback Switch", mute_elements);589	if (ret) {590		dev_err(&chip->dev->dev, "cannot add control.\n");591		kfree(rt);592		return ret;593	}594 595	i = 0;596	while (elements[i].name) {597		ret = snd_ctl_add(chip->card, snd_ctl_new1(&elements[i], rt));598		if (ret < 0) {599			kfree(rt);600			dev_err(&chip->dev->dev, "cannot add control.\n");601			return ret;602		}603		i++;604	}605 606	chip->control = rt;607	return 0;608}609 610void usb6fire_control_abort(struct sfire_chip *chip)611{}612 613void usb6fire_control_destroy(struct sfire_chip *chip)614{615	kfree(chip->control);616	chip->control = NULL;617}618