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