2011-01-24 18:45:30 +01:00
/*
* Linux driver for TerraTec DMX 6F ire USB
*
* Mixer control
*
* Author : Torsten Schenk < torsten . schenk @ zoho . com >
* Created : Jan 01 , 2011
* Copyright : ( C ) Torsten Schenk
*
2012-02-22 15:21:12 +01:00
* Thanks to :
* - Holger Ruckdeschel : he found out how to control individual channel
* volumes and introduced mute switch
*
2011-01-24 18:45:30 +01:00
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/interrupt.h>
# include <sound/control.h>
2012-02-22 15:20:54 +01:00
# include <sound/tlv.h>
2011-01-24 18:45:30 +01:00
# include "control.h"
# include "comm.h"
# include "chip.h"
2014-10-20 18:21:27 +02:00
static const char * const opt_coax_texts [ 2 ] = { " Optical " , " Coax " } ;
static const char * const line_phono_texts [ 2 ] = { " Line " , " Phono " } ;
2011-01-24 18:45:30 +01:00
/*
* data that needs to be sent to device . sets up card internal stuff .
* values dumped from windows driver and filtered by trial ' n ' error .
*/
static const struct {
u8 type ;
u8 reg ;
u8 value ;
}
init_data [ ] = {
{ 0x22 , 0x00 , 0x00 } , { 0x20 , 0x00 , 0x08 } , { 0x22 , 0x01 , 0x01 } ,
{ 0x20 , 0x01 , 0x08 } , { 0x22 , 0x02 , 0x00 } , { 0x20 , 0x02 , 0x08 } ,
{ 0x22 , 0x03 , 0x00 } , { 0x20 , 0x03 , 0x08 } , { 0x22 , 0x04 , 0x00 } ,
{ 0x20 , 0x04 , 0x08 } , { 0x22 , 0x05 , 0x01 } , { 0x20 , 0x05 , 0x08 } ,
{ 0x22 , 0x04 , 0x01 } , { 0x12 , 0x04 , 0x00 } , { 0x12 , 0x05 , 0x00 } ,
2012-02-22 15:21:12 +01:00
{ 0x12 , 0x0d , 0x38 } , { 0x12 , 0x21 , 0x82 } , { 0x12 , 0x22 , 0x80 } ,
2011-01-24 18:45:30 +01:00
{ 0x12 , 0x23 , 0x00 } , { 0x12 , 0x06 , 0x02 } , { 0x12 , 0x03 , 0x00 } ,
{ 0x12 , 0x02 , 0x00 } , { 0x22 , 0x03 , 0x01 } ,
{ 0 } /* TERMINATING ENTRY */
} ;
2011-04-04 11:50:53 +02:00
static const int rates_altsetting [ ] = { 1 , 1 , 2 , 2 , 3 , 3 } ;
/* values to write to soundcard register for all samplerates */
static const u16 rates_6fire_vl [ ] = { 0x00 , 0x01 , 0x00 , 0x01 , 0x00 , 0x01 } ;
static const u16 rates_6fire_vh [ ] = { 0x11 , 0x11 , 0x10 , 0x10 , 0x00 , 0x00 } ;
2012-02-22 15:20:54 +01:00
static DECLARE_TLV_DB_MINMAX ( tlv_output , - 9000 , 0 ) ;
2012-02-22 15:21:30 +01:00
static DECLARE_TLV_DB_MINMAX ( tlv_input , - 1500 , 1500 ) ;
2012-02-22 15:20:54 +01:00
2011-04-04 11:50:53 +02:00
enum {
DIGITAL_THRU_ONLY_SAMPLERATE = 3
} ;
2012-02-22 15:21:12 +01:00
static void usb6fire_control_output_vol_update ( struct control_runtime * rt )
2011-01-24 18:45:30 +01:00
{
struct comm_runtime * comm_rt = rt - > chip - > comm ;
2012-02-22 15:21:12 +01:00
int i ;
if ( comm_rt )
for ( i = 0 ; i < 6 ; i + + )
if ( ! ( rt - > ovol_updated & ( 1 < < i ) ) ) {
comm_rt - > write8 ( comm_rt , 0x12 , 0x0f + i ,
180 - rt - > output_vol [ i ] ) ;
rt - > ovol_updated | = 1 < < i ;
}
2011-01-24 18:45:30 +01:00
}
2012-02-22 15:21:23 +01:00
static void usb6fire_control_output_mute_update ( struct control_runtime * rt )
{
struct comm_runtime * comm_rt = rt - > chip - > comm ;
if ( comm_rt )
comm_rt - > write8 ( comm_rt , 0x12 , 0x0e , ~ rt - > output_mute ) ;
}
2012-02-22 15:21:30 +01:00
static void usb6fire_control_input_vol_update ( struct control_runtime * rt )
{
struct comm_runtime * comm_rt = rt - > chip - > comm ;
int i ;
if ( comm_rt )
for ( i = 0 ; i < 2 ; i + + )
if ( ! ( rt - > ivol_updated & ( 1 < < i ) ) ) {
comm_rt - > write8 ( comm_rt , 0x12 , 0x1c + i ,
rt - > input_vol [ i ] & 0x3f ) ;
rt - > ivol_updated | = 1 < < i ;
}
}
2011-01-24 18:45:30 +01:00
static void usb6fire_control_line_phono_update ( struct control_runtime * rt )
{
struct comm_runtime * comm_rt = rt - > chip - > comm ;
if ( comm_rt ) {
comm_rt - > write8 ( comm_rt , 0x22 , 0x02 , rt - > line_phono_switch ) ;
comm_rt - > write8 ( comm_rt , 0x21 , 0x02 , rt - > line_phono_switch ) ;
}
}
static void usb6fire_control_opt_coax_update ( struct control_runtime * rt )
{
struct comm_runtime * comm_rt = rt - > chip - > comm ;
if ( comm_rt ) {
comm_rt - > write8 ( comm_rt , 0x22 , 0x00 , rt - > opt_coax_switch ) ;
comm_rt - > write8 ( comm_rt , 0x21 , 0x00 , rt - > opt_coax_switch ) ;
}
}
2011-04-04 11:50:53 +02:00
static int usb6fire_control_set_rate ( struct control_runtime * rt , int rate )
{
int ret ;
struct usb_device * device = rt - > chip - > dev ;
struct comm_runtime * comm_rt = rt - > chip - > comm ;
if ( rate < 0 | | rate > = CONTROL_N_RATES )
return - EINVAL ;
ret = usb_set_interface ( device , 1 , rates_altsetting [ rate ] ) ;
if ( ret < 0 )
return ret ;
/* set soundcard clock */
ret = comm_rt - > write16 ( comm_rt , 0x02 , 0x01 , rates_6fire_vl [ rate ] ,
rates_6fire_vh [ rate ] ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int usb6fire_control_set_channels (
struct control_runtime * rt , int n_analog_out ,
int n_analog_in , bool spdif_out , bool spdif_in )
{
int ret ;
struct comm_runtime * comm_rt = rt - > chip - > comm ;
/* enable analog inputs and outputs
* ( one bit per stereo - channel ) */
ret = comm_rt - > write16 ( comm_rt , 0x02 , 0x02 ,
( 1 < < ( n_analog_out / 2 ) ) - 1 ,
( 1 < < ( n_analog_in / 2 ) ) - 1 ) ;
if ( ret < 0 )
return ret ;
/* disable digital inputs and outputs */
/* TODO: use spdif_x to enable/disable digital channels */
ret = comm_rt - > write16 ( comm_rt , 0x02 , 0x03 , 0x00 , 0x00 ) ;
if ( ret < 0 )
return ret ;
return 0 ;
}
static int usb6fire_control_streaming_update ( struct control_runtime * rt )
{
struct comm_runtime * comm_rt = rt - > chip - > comm ;
if ( comm_rt ) {
if ( ! rt - > usb_streaming & & rt - > digital_thru_switch )
usb6fire_control_set_rate ( rt ,
DIGITAL_THRU_ONLY_SAMPLERATE ) ;
return comm_rt - > write16 ( comm_rt , 0x02 , 0x00 , 0x00 ,
( rt - > usb_streaming ? 0x01 : 0x00 ) |
( rt - > digital_thru_switch ? 0x08 : 0x00 ) ) ;
}
return - EINVAL ;
}
2012-02-22 15:21:12 +01:00
static int usb6fire_control_output_vol_info ( struct snd_kcontrol * kcontrol ,
2011-01-24 18:45:30 +01:00
struct snd_ctl_elem_info * uinfo )
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
2012-02-22 15:21:12 +01:00
uinfo - > count = 2 ;
2011-01-24 18:45:30 +01:00
uinfo - > value . integer . min = 0 ;
2012-02-22 15:20:54 +01:00
uinfo - > value . integer . max = 180 ;
2011-01-24 18:45:30 +01:00
return 0 ;
}
2012-02-22 15:21:12 +01:00
static int usb6fire_control_output_vol_put ( struct snd_kcontrol * kcontrol ,
2011-01-24 18:45:30 +01:00
struct snd_ctl_elem_value * ucontrol )
{
struct control_runtime * rt = snd_kcontrol_chip ( kcontrol ) ;
2012-02-22 15:21:12 +01:00
unsigned int ch = kcontrol - > private_value ;
2011-01-24 18:45:30 +01:00
int changed = 0 ;
2012-02-22 15:21:12 +01:00
if ( ch > 4 ) {
2014-02-26 15:51:04 +01:00
dev_err ( & rt - > chip - > dev - > dev ,
" Invalid channel in volume control. " ) ;
2012-02-22 15:21:12 +01:00
return - EINVAL ;
}
if ( rt - > output_vol [ ch ] ! = ucontrol - > value . integer . value [ 0 ] ) {
rt - > output_vol [ ch ] = ucontrol - > value . integer . value [ 0 ] ;
rt - > ovol_updated & = ~ ( 1 < < ch ) ;
2011-01-24 18:45:30 +01:00
changed = 1 ;
}
2012-02-22 15:21:12 +01:00
if ( rt - > output_vol [ ch + 1 ] ! = ucontrol - > value . integer . value [ 1 ] ) {
rt - > output_vol [ ch + 1 ] = ucontrol - > value . integer . value [ 1 ] ;
rt - > ovol_updated & = ~ ( 2 < < ch ) ;
changed = 1 ;
}
if ( changed )
usb6fire_control_output_vol_update ( rt ) ;
2011-01-24 18:45:30 +01:00
return changed ;
}
2012-02-22 15:21:12 +01:00
static int usb6fire_control_output_vol_get ( struct snd_kcontrol * kcontrol ,
2011-01-24 18:45:30 +01:00
struct snd_ctl_elem_value * ucontrol )
{
struct control_runtime * rt = snd_kcontrol_chip ( kcontrol ) ;
2012-02-22 15:21:12 +01:00
unsigned int ch = kcontrol - > private_value ;
if ( ch > 4 ) {
2014-02-26 15:51:04 +01:00
dev_err ( & rt - > chip - > dev - > dev ,
" Invalid channel in volume control. " ) ;
2012-02-22 15:21:12 +01:00
return - EINVAL ;
}
ucontrol - > value . integer . value [ 0 ] = rt - > output_vol [ ch ] ;
ucontrol - > value . integer . value [ 1 ] = rt - > output_vol [ ch + 1 ] ;
2011-01-24 18:45:30 +01:00
return 0 ;
}
2012-02-22 15:21:23 +01:00
static int usb6fire_control_output_mute_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct control_runtime * rt = snd_kcontrol_chip ( kcontrol ) ;
unsigned int ch = kcontrol - > private_value ;
u8 old = rt - > output_mute ;
u8 value = 0 ;
if ( ch > 4 ) {
2014-02-26 15:51:04 +01:00
dev_err ( & rt - > chip - > dev - > dev ,
" Invalid channel in volume control. " ) ;
2012-02-22 15:21:23 +01:00
return - EINVAL ;
}
rt - > output_mute & = ~ ( 3 < < ch ) ;
if ( ucontrol - > value . integer . value [ 0 ] )
value | = 1 ;
if ( ucontrol - > value . integer . value [ 1 ] )
value | = 2 ;
rt - > output_mute | = value < < ch ;
if ( rt - > output_mute ! = old )
usb6fire_control_output_mute_update ( rt ) ;
return rt - > output_mute ! = old ;
}
static int usb6fire_control_output_mute_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct control_runtime * rt = snd_kcontrol_chip ( kcontrol ) ;
unsigned int ch = kcontrol - > private_value ;
u8 value = rt - > output_mute > > ch ;
if ( ch > 4 ) {
2014-02-26 15:51:04 +01:00
dev_err ( & rt - > chip - > dev - > dev ,
" Invalid channel in volume control. " ) ;
2012-02-22 15:21:23 +01:00
return - EINVAL ;
}
ucontrol - > value . integer . value [ 0 ] = 1 & value ;
value > > = 1 ;
ucontrol - > value . integer . value [ 1 ] = 1 & value ;
return 0 ;
}
2012-02-22 15:21:30 +01:00
static int usb6fire_control_input_vol_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
uinfo - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
uinfo - > count = 2 ;
uinfo - > value . integer . min = 0 ;
uinfo - > value . integer . max = 30 ;
return 0 ;
}
static int usb6fire_control_input_vol_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct control_runtime * rt = snd_kcontrol_chip ( kcontrol ) ;
int changed = 0 ;
if ( rt - > input_vol [ 0 ] ! = ucontrol - > value . integer . value [ 0 ] ) {
rt - > input_vol [ 0 ] = ucontrol - > value . integer . value [ 0 ] - 15 ;
rt - > ivol_updated & = ~ ( 1 < < 0 ) ;
changed = 1 ;
}
if ( rt - > input_vol [ 1 ] ! = ucontrol - > value . integer . value [ 1 ] ) {
rt - > input_vol [ 1 ] = ucontrol - > value . integer . value [ 1 ] - 15 ;
rt - > ivol_updated & = ~ ( 1 < < 1 ) ;
changed = 1 ;
}
if ( changed )
usb6fire_control_input_vol_update ( rt ) ;
return changed ;
}
static int usb6fire_control_input_vol_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct control_runtime * rt = snd_kcontrol_chip ( kcontrol ) ;
ucontrol - > value . integer . value [ 0 ] = rt - > input_vol [ 0 ] + 15 ;
ucontrol - > value . integer . value [ 1 ] = rt - > input_vol [ 1 ] + 15 ;
return 0 ;
}
2011-01-24 18:45:30 +01:00
static int usb6fire_control_line_phono_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
2014-10-20 18:21:27 +02:00
return snd_ctl_enum_info ( uinfo , 1 , 2 , line_phono_texts ) ;
2011-01-24 18:45:30 +01:00
}
static int usb6fire_control_line_phono_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct control_runtime * rt = snd_kcontrol_chip ( kcontrol ) ;
int changed = 0 ;
if ( rt - > line_phono_switch ! = ucontrol - > value . integer . value [ 0 ] ) {
rt - > line_phono_switch = ucontrol - > value . integer . value [ 0 ] ;
usb6fire_control_line_phono_update ( rt ) ;
changed = 1 ;
}
return changed ;
}
static int usb6fire_control_line_phono_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct control_runtime * rt = snd_kcontrol_chip ( kcontrol ) ;
ucontrol - > value . integer . value [ 0 ] = rt - > line_phono_switch ;
return 0 ;
}
static int usb6fire_control_opt_coax_info ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_info * uinfo )
{
2014-10-20 18:21:27 +02:00
return snd_ctl_enum_info ( uinfo , 1 , 2 , opt_coax_texts ) ;
2011-01-24 18:45:30 +01:00
}
static int usb6fire_control_opt_coax_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct control_runtime * rt = snd_kcontrol_chip ( kcontrol ) ;
int changed = 0 ;
if ( rt - > opt_coax_switch ! = ucontrol - > value . enumerated . item [ 0 ] ) {
rt - > opt_coax_switch = ucontrol - > value . enumerated . item [ 0 ] ;
usb6fire_control_opt_coax_update ( rt ) ;
changed = 1 ;
}
return changed ;
}
static int usb6fire_control_opt_coax_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct control_runtime * rt = snd_kcontrol_chip ( kcontrol ) ;
ucontrol - > value . enumerated . item [ 0 ] = rt - > opt_coax_switch ;
return 0 ;
}
2011-04-04 11:50:53 +02:00
static int usb6fire_control_digital_thru_put ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct control_runtime * rt = snd_kcontrol_chip ( kcontrol ) ;
int changed = 0 ;
if ( rt - > digital_thru_switch ! = ucontrol - > value . integer . value [ 0 ] ) {
rt - > digital_thru_switch = ucontrol - > value . integer . value [ 0 ] ;
usb6fire_control_streaming_update ( rt ) ;
changed = 1 ;
}
return changed ;
}
static int usb6fire_control_digital_thru_get ( struct snd_kcontrol * kcontrol ,
struct snd_ctl_elem_value * ucontrol )
{
struct control_runtime * rt = snd_kcontrol_chip ( kcontrol ) ;
ucontrol - > value . integer . value [ 0 ] = rt - > digital_thru_switch ;
return 0 ;
}
2012-12-06 12:35:28 -05:00
static struct snd_kcontrol_new vol_elements [ ] = {
2011-01-24 18:45:30 +01:00
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
2012-02-22 15:21:12 +01:00
. name = " Analog Playback Volume " ,
2011-01-24 18:45:30 +01:00
. index = 0 ,
2012-02-22 15:21:12 +01:00
. private_value = 0 ,
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ ,
. info = usb6fire_control_output_vol_info ,
. get = usb6fire_control_output_vol_get ,
. put = usb6fire_control_output_vol_put ,
. tlv = { . p = tlv_output }
} ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Analog Playback Volume " ,
. index = 1 ,
. private_value = 2 ,
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ ,
. info = usb6fire_control_output_vol_info ,
. get = usb6fire_control_output_vol_get ,
. put = usb6fire_control_output_vol_put ,
. tlv = { . p = tlv_output }
} ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Analog Playback Volume " ,
. index = 2 ,
. private_value = 4 ,
2012-02-22 15:20:54 +01:00
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ ,
2012-02-22 15:21:12 +01:00
. info = usb6fire_control_output_vol_info ,
. get = usb6fire_control_output_vol_get ,
. put = usb6fire_control_output_vol_put ,
2012-02-22 15:20:54 +01:00
. tlv = { . p = tlv_output }
2011-01-24 18:45:30 +01:00
} ,
2012-02-22 15:21:12 +01:00
{ }
} ;
2012-12-06 12:35:28 -05:00
static struct snd_kcontrol_new mute_elements [ ] = {
2012-02-22 15:21:23 +01:00
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Analog Playback Switch " ,
. index = 0 ,
. private_value = 0 ,
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE ,
. info = snd_ctl_boolean_stereo_info ,
. get = usb6fire_control_output_mute_get ,
. put = usb6fire_control_output_mute_put ,
} ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Analog Playback Switch " ,
. index = 1 ,
. private_value = 2 ,
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE ,
. info = snd_ctl_boolean_stereo_info ,
. get = usb6fire_control_output_mute_get ,
. put = usb6fire_control_output_mute_put ,
} ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Analog Playback Switch " ,
. index = 2 ,
. private_value = 4 ,
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE ,
. info = snd_ctl_boolean_stereo_info ,
. get = usb6fire_control_output_mute_get ,
. put = usb6fire_control_output_mute_put ,
} ,
{ }
} ;
2012-12-06 12:35:28 -05:00
static struct snd_kcontrol_new elements [ ] = {
2011-01-24 18:45:30 +01:00
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Line/Phono Capture Route " ,
. index = 0 ,
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE ,
. info = usb6fire_control_line_phono_info ,
. get = usb6fire_control_line_phono_get ,
. put = usb6fire_control_line_phono_put
} ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Opt/Coax Capture Route " ,
. index = 0 ,
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE ,
. info = usb6fire_control_opt_coax_info ,
. get = usb6fire_control_opt_coax_get ,
. put = usb6fire_control_opt_coax_put
} ,
2011-04-04 11:50:53 +02:00
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Digital Thru Playback Route " ,
. index = 0 ,
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE ,
. info = snd_ctl_boolean_mono_info ,
. get = usb6fire_control_digital_thru_get ,
. put = usb6fire_control_digital_thru_put
} ,
2012-02-22 15:21:30 +01:00
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " Analog Capture Volume " ,
. index = 0 ,
. access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ ,
. info = usb6fire_control_input_vol_info ,
. get = usb6fire_control_input_vol_get ,
. put = usb6fire_control_input_vol_put ,
. tlv = { . p = tlv_input }
} ,
2011-01-24 18:45:30 +01:00
{ }
} ;
2012-02-22 15:21:12 +01:00
static int usb6fire_control_add_virtual (
struct control_runtime * rt ,
struct snd_card * card ,
char * name ,
struct snd_kcontrol_new * elems )
{
int ret ;
int i ;
struct snd_kcontrol * vmaster =
snd_ctl_make_virtual_master ( name , tlv_output ) ;
struct snd_kcontrol * control ;
if ( ! vmaster )
return - ENOMEM ;
ret = snd_ctl_add ( card , vmaster ) ;
if ( ret < 0 )
return ret ;
i = 0 ;
while ( elems [ i ] . name ) {
control = snd_ctl_new1 ( & elems [ i ] , rt ) ;
if ( ! control )
return - ENOMEM ;
ret = snd_ctl_add ( card , control ) ;
if ( ret < 0 )
return ret ;
ret = snd_ctl_add_slave ( vmaster , control ) ;
if ( ret < 0 )
return ret ;
i + + ;
}
return 0 ;
}
2012-12-06 12:35:28 -05:00
int usb6fire_control_init ( struct sfire_chip * chip )
2011-01-24 18:45:30 +01:00
{
int i ;
int ret ;
struct control_runtime * rt = kzalloc ( sizeof ( struct control_runtime ) ,
GFP_KERNEL ) ;
struct comm_runtime * comm_rt = chip - > comm ;
if ( ! rt )
return - ENOMEM ;
rt - > chip = chip ;
2011-04-04 11:50:53 +02:00
rt - > update_streaming = usb6fire_control_streaming_update ;
rt - > set_rate = usb6fire_control_set_rate ;
rt - > set_channels = usb6fire_control_set_channels ;
2011-01-24 18:45:30 +01:00
i = 0 ;
while ( init_data [ i ] . type ) {
comm_rt - > write8 ( comm_rt , init_data [ i ] . type , init_data [ i ] . reg ,
init_data [ i ] . value ) ;
i + + ;
}
usb6fire_control_opt_coax_update ( rt ) ;
usb6fire_control_line_phono_update ( rt ) ;
2012-02-22 15:21:12 +01:00
usb6fire_control_output_vol_update ( rt ) ;
2012-02-22 15:21:23 +01:00
usb6fire_control_output_mute_update ( rt ) ;
2012-02-22 15:21:30 +01:00
usb6fire_control_input_vol_update ( rt ) ;
2011-04-04 11:50:53 +02:00
usb6fire_control_streaming_update ( rt ) ;
2011-01-24 18:45:30 +01:00
2012-02-22 15:21:12 +01:00
ret = usb6fire_control_add_virtual ( rt , chip - > card ,
" Master Playback Volume " , vol_elements ) ;
if ( ret ) {
2014-02-26 15:51:04 +01:00
dev_err ( & chip - > dev - > dev , " cannot add control. \n " ) ;
2012-02-22 15:21:12 +01:00
kfree ( rt ) ;
2012-02-22 15:21:23 +01:00
return ret ;
}
ret = usb6fire_control_add_virtual ( rt , chip - > card ,
" Master Playback Switch " , mute_elements ) ;
if ( ret ) {
2014-02-26 15:51:04 +01:00
dev_err ( & chip - > dev - > dev , " cannot add control. \n " ) ;
2012-02-22 15:21:23 +01:00
kfree ( rt ) ;
2012-02-22 15:21:12 +01:00
return ret ;
}
2011-01-24 18:45:30 +01:00
i = 0 ;
while ( elements [ i ] . name ) {
ret = snd_ctl_add ( chip - > card , snd_ctl_new1 ( & elements [ i ] , rt ) ) ;
if ( ret < 0 ) {
kfree ( rt ) ;
2014-02-26 15:51:04 +01:00
dev_err ( & chip - > dev - > dev , " cannot add control. \n " ) ;
2011-01-24 18:45:30 +01:00
return ret ;
}
i + + ;
}
chip - > control = rt ;
return 0 ;
}
void usb6fire_control_abort ( struct sfire_chip * chip )
{ }
void usb6fire_control_destroy ( struct sfire_chip * chip )
{
kfree ( chip - > control ) ;
chip - > control = NULL ;
}