2014-11-29 00:59:29 +09:00
/*
2015-12-15 23:56:17 +09:00
* oxfw - spkr . c - a part of driver for OXFW970 / 971 based devices
2014-11-29 00:59:29 +09:00
*
* Copyright ( c ) Clemens Ladisch < clemens @ ladisch . de >
* Licensed under the terms of the GNU General Public License , version 2.
*/
# include "oxfw.h"
2015-12-16 20:37:55 +09:00
struct fw_spkr {
bool mute ;
s16 volume [ 6 ] ;
s16 volume_min ;
s16 volume_max ;
2015-12-16 20:37:56 +09:00
unsigned int mixer_channels ;
u8 mute_fb_id ;
u8 volume_fb_id ;
2015-12-16 20:37:55 +09:00
} ;
2014-11-29 00:59:29 +09:00
enum control_action { CTL_READ , CTL_WRITE } ;
enum control_attribute {
CTL_MIN = 0x02 ,
CTL_MAX = 0x03 ,
CTL_CURRENT = 0x10 ,
} ;
2015-12-15 23:56:19 +09:00
static int avc_audio_feature_mute ( struct fw_unit * unit , u8 fb_id , bool * value ,
enum control_action action )
2014-11-29 00:59:29 +09:00
{
u8 * buf ;
u8 response_ok ;
int err ;
buf = kmalloc ( 11 , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
if ( action = = CTL_READ ) {
buf [ 0 ] = 0x01 ; /* AV/C, STATUS */
response_ok = 0x0c ; /* STABLE */
} else {
buf [ 0 ] = 0x00 ; /* AV/C, CONTROL */
response_ok = 0x09 ; /* ACCEPTED */
}
buf [ 1 ] = 0x08 ; /* audio unit 0 */
buf [ 2 ] = 0xb8 ; /* FUNCTION BLOCK */
buf [ 3 ] = 0x81 ; /* function block type: feature */
2015-12-15 23:56:19 +09:00
buf [ 4 ] = fb_id ; /* function block ID */
2014-11-29 00:59:29 +09:00
buf [ 5 ] = 0x10 ; /* control attribute: current */
buf [ 6 ] = 0x02 ; /* selector length */
buf [ 7 ] = 0x00 ; /* audio channel number */
buf [ 8 ] = 0x01 ; /* control selector: mute */
buf [ 9 ] = 0x01 ; /* control data length */
if ( action = = CTL_READ )
buf [ 10 ] = 0xff ;
else
buf [ 10 ] = * value ? 0x70 : 0x60 ;
2015-12-15 23:56:19 +09:00
err = fcp_avc_transaction ( unit , buf , 11 , buf , 11 , 0x3fe ) ;
2014-11-29 00:59:29 +09:00
if ( err < 0 )
goto error ;
if ( err < 11 ) {
2015-12-15 23:56:19 +09:00
dev_err ( & unit - > device , " short FCP response \n " ) ;
2014-11-29 00:59:29 +09:00
err = - EIO ;
goto error ;
}
if ( buf [ 0 ] ! = response_ok ) {
2015-12-15 23:56:19 +09:00
dev_err ( & unit - > device , " mute command failed \n " ) ;
2014-11-29 00:59:29 +09:00
err = - EIO ;
goto error ;
}
if ( action = = CTL_READ )
* value = buf [ 10 ] = = 0x70 ;
err = 0 ;
error :
kfree ( buf ) ;
return err ;
}
2015-12-15 23:56:19 +09:00
static int avc_audio_feature_volume ( struct fw_unit * unit , u8 fb_id , s16 * value ,
unsigned int channel ,
enum control_attribute attribute ,
enum control_action action )
2014-11-29 00:59:29 +09:00
{
u8 * buf ;
u8 response_ok ;
int err ;
buf = kmalloc ( 12 , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
if ( action = = CTL_READ ) {
buf [ 0 ] = 0x01 ; /* AV/C, STATUS */
response_ok = 0x0c ; /* STABLE */
} else {
buf [ 0 ] = 0x00 ; /* AV/C, CONTROL */
response_ok = 0x09 ; /* ACCEPTED */
}
buf [ 1 ] = 0x08 ; /* audio unit 0 */
buf [ 2 ] = 0xb8 ; /* FUNCTION BLOCK */
buf [ 3 ] = 0x81 ; /* function block type: feature */
2015-12-15 23:56:19 +09:00
buf [ 4 ] = fb_id ; /* function block ID */
2014-11-29 00:59:29 +09:00
buf [ 5 ] = attribute ; /* control attribute */
buf [ 6 ] = 0x02 ; /* selector length */
buf [ 7 ] = channel ; /* audio channel number */
buf [ 8 ] = 0x02 ; /* control selector: volume */
buf [ 9 ] = 0x02 ; /* control data length */
if ( action = = CTL_READ ) {
buf [ 10 ] = 0xff ;
buf [ 11 ] = 0xff ;
} else {
buf [ 10 ] = * value > > 8 ;
buf [ 11 ] = * value ;
}
2015-12-15 23:56:19 +09:00
err = fcp_avc_transaction ( unit , buf , 12 , buf , 12 , 0x3fe ) ;
2014-11-29 00:59:29 +09:00
if ( err < 0 )
goto error ;
if ( err < 12 ) {
2015-12-15 23:56:19 +09:00
dev_err ( & unit - > device , " short FCP response \n " ) ;
2014-11-29 00:59:29 +09:00
err = - EIO ;
goto error ;
}
if ( buf [ 0 ] ! = response_ok ) {
2015-12-15 23:56:19 +09:00
dev_err ( & unit - > device , " volume command failed \n " ) ;
2014-11-29 00:59:29 +09:00
err = - EIO ;
goto error ;
}
if ( action = = CTL_READ )
* value = ( buf [ 10 ] < < 8 ) | buf [ 11 ] ;
err = 0 ;
error :
kfree ( buf ) ;
return err ;
}
2015-12-15 23:56:18 +09:00
static int spkr_mute_get ( struct snd_kcontrol * control ,
2014-11-29 00:59:29 +09:00
struct snd_ctl_elem_value * value )
{
struct snd_oxfw * oxfw = control - > private_data ;
2015-12-16 20:37:55 +09:00
struct fw_spkr * spkr = oxfw - > spec ;
2014-11-29 00:59:29 +09:00
2015-12-16 20:37:55 +09:00
value - > value . integer . value [ 0 ] = ! spkr - > mute ;
2014-11-29 00:59:29 +09:00
return 0 ;
}
2015-12-15 23:56:18 +09:00
static int spkr_mute_put ( struct snd_kcontrol * control ,
2014-11-29 00:59:29 +09:00
struct snd_ctl_elem_value * value )
{
struct snd_oxfw * oxfw = control - > private_data ;
2015-12-16 20:37:55 +09:00
struct fw_spkr * spkr = oxfw - > spec ;
2014-11-29 00:59:29 +09:00
bool mute ;
int err ;
mute = ! value - > value . integer . value [ 0 ] ;
2015-12-16 20:37:55 +09:00
if ( mute = = spkr - > mute )
2014-11-29 00:59:29 +09:00
return 0 ;
2015-12-16 20:37:56 +09:00
err = avc_audio_feature_mute ( oxfw - > unit , spkr - > mute_fb_id , & mute ,
CTL_WRITE ) ;
2014-11-29 00:59:29 +09:00
if ( err < 0 )
return err ;
2015-12-16 20:37:55 +09:00
spkr - > mute = mute ;
2014-11-29 00:59:29 +09:00
return 1 ;
}
2015-12-15 23:56:18 +09:00
static int spkr_volume_info ( struct snd_kcontrol * control ,
2014-11-29 00:59:29 +09:00
struct snd_ctl_elem_info * info )
{
struct snd_oxfw * oxfw = control - > private_data ;
2015-12-16 20:37:55 +09:00
struct fw_spkr * spkr = oxfw - > spec ;
2014-11-29 00:59:29 +09:00
info - > type = SNDRV_CTL_ELEM_TYPE_INTEGER ;
2015-12-16 20:37:56 +09:00
info - > count = spkr - > mixer_channels ;
2015-12-16 20:37:55 +09:00
info - > value . integer . min = spkr - > volume_min ;
info - > value . integer . max = spkr - > volume_max ;
2014-11-29 00:59:29 +09:00
return 0 ;
}
static const u8 channel_map [ 6 ] = { 0 , 1 , 4 , 5 , 2 , 3 } ;
2015-12-15 23:56:18 +09:00
static int spkr_volume_get ( struct snd_kcontrol * control ,
2014-11-29 00:59:29 +09:00
struct snd_ctl_elem_value * value )
{
struct snd_oxfw * oxfw = control - > private_data ;
2015-12-16 20:37:55 +09:00
struct fw_spkr * spkr = oxfw - > spec ;
2014-11-29 00:59:29 +09:00
unsigned int i ;
2015-12-16 20:37:56 +09:00
for ( i = 0 ; i < spkr - > mixer_channels ; + + i )
2015-12-16 20:37:55 +09:00
value - > value . integer . value [ channel_map [ i ] ] = spkr - > volume [ i ] ;
2014-11-29 00:59:29 +09:00
return 0 ;
}
2015-12-15 23:56:18 +09:00
static int spkr_volume_put ( struct snd_kcontrol * control ,
2014-11-29 00:59:29 +09:00
struct snd_ctl_elem_value * value )
{
struct snd_oxfw * oxfw = control - > private_data ;
2015-12-16 20:37:55 +09:00
struct fw_spkr * spkr = oxfw - > spec ;
2014-11-29 00:59:29 +09:00
unsigned int i , changed_channels ;
bool equal_values = true ;
s16 volume ;
int err ;
2015-12-16 20:37:56 +09:00
for ( i = 0 ; i < spkr - > mixer_channels ; + + i ) {
2015-12-16 20:37:55 +09:00
if ( value - > value . integer . value [ i ] < spkr - > volume_min | |
value - > value . integer . value [ i ] > spkr - > volume_max )
2014-11-29 00:59:29 +09:00
return - EINVAL ;
if ( value - > value . integer . value [ i ] ! =
value - > value . integer . value [ 0 ] )
equal_values = false ;
}
changed_channels = 0 ;
2015-12-16 20:37:56 +09:00
for ( i = 0 ; i < spkr - > mixer_channels ; + + i )
2014-11-29 00:59:29 +09:00
if ( value - > value . integer . value [ channel_map [ i ] ] ! =
2015-12-16 20:37:55 +09:00
spkr - > volume [ i ] )
2014-11-29 00:59:29 +09:00
changed_channels | = 1 < < ( i + 1 ) ;
if ( equal_values & & changed_channels ! = 0 )
changed_channels = 1 < < 0 ;
2015-12-16 20:37:56 +09:00
for ( i = 0 ; i < = spkr - > mixer_channels ; + + i ) {
2014-11-29 00:59:29 +09:00
volume = value - > value . integer . value [ channel_map [ i ? i - 1 : 0 ] ] ;
if ( changed_channels & ( 1 < < i ) ) {
2015-12-15 23:56:19 +09:00
err = avc_audio_feature_volume ( oxfw - > unit ,
2015-12-16 20:37:56 +09:00
spkr - > volume_fb_id , & volume ,
2015-12-15 23:56:19 +09:00
i , CTL_CURRENT , CTL_WRITE ) ;
2014-11-29 00:59:29 +09:00
if ( err < 0 )
return err ;
}
if ( i > 0 )
2015-12-16 20:37:55 +09:00
spkr - > volume [ i - 1 ] = volume ;
2014-11-29 00:59:29 +09:00
}
return changed_channels ! = 0 ;
}
2015-12-16 20:37:56 +09:00
int snd_oxfw_add_spkr ( struct snd_oxfw * oxfw , bool is_lacie )
2014-11-29 00:59:29 +09:00
{
static const struct snd_kcontrol_new controls [ ] = {
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " PCM Playback Switch " ,
. info = snd_ctl_boolean_mono_info ,
2015-12-15 23:56:18 +09:00
. get = spkr_mute_get ,
. put = spkr_mute_put ,
2014-11-29 00:59:29 +09:00
} ,
{
. iface = SNDRV_CTL_ELEM_IFACE_MIXER ,
. name = " PCM Playback Volume " ,
2015-12-15 23:56:18 +09:00
. info = spkr_volume_info ,
. get = spkr_volume_get ,
. put = spkr_volume_put ,
2014-11-29 00:59:29 +09:00
} ,
} ;
2015-12-16 20:37:55 +09:00
struct fw_spkr * spkr ;
2014-11-29 00:59:29 +09:00
unsigned int i , first_ch ;
int err ;
2018-10-03 08:21:53 +09:00
spkr = devm_kzalloc ( & oxfw - > card - > card_dev , sizeof ( struct fw_spkr ) ,
GFP_KERNEL ) ;
if ( ! spkr )
2015-12-16 20:37:55 +09:00
return - ENOMEM ;
oxfw - > spec = spkr ;
2015-12-16 20:37:56 +09:00
if ( is_lacie ) {
spkr - > mixer_channels = 1 ;
spkr - > mute_fb_id = 0x01 ;
spkr - > volume_fb_id = 0x01 ;
} else {
spkr - > mixer_channels = 6 ;
spkr - > mute_fb_id = 0x01 ;
spkr - > volume_fb_id = 0x02 ;
}
err = avc_audio_feature_volume ( oxfw - > unit , spkr - > volume_fb_id ,
& spkr - > volume_min , 0 , CTL_MIN , CTL_READ ) ;
2014-11-29 00:59:29 +09:00
if ( err < 0 )
return err ;
2015-12-16 20:37:56 +09:00
err = avc_audio_feature_volume ( oxfw - > unit , spkr - > volume_fb_id ,
& spkr - > volume_max , 0 , CTL_MAX , CTL_READ ) ;
2014-11-29 00:59:29 +09:00
if ( err < 0 )
return err ;
2015-12-16 20:37:56 +09:00
err = avc_audio_feature_mute ( oxfw - > unit , spkr - > mute_fb_id , & spkr - > mute ,
CTL_READ ) ;
2014-11-29 00:59:29 +09:00
if ( err < 0 )
return err ;
2015-12-16 20:37:56 +09:00
first_ch = spkr - > mixer_channels = = 1 ? 0 : 1 ;
for ( i = 0 ; i < spkr - > mixer_channels ; + + i ) {
err = avc_audio_feature_volume ( oxfw - > unit , spkr - > volume_fb_id ,
& spkr - > volume [ i ] , first_ch + i ,
CTL_CURRENT , CTL_READ ) ;
2014-11-29 00:59:29 +09:00
if ( err < 0 )
return err ;
}
for ( i = 0 ; i < ARRAY_SIZE ( controls ) ; + + i ) {
err = snd_ctl_add ( oxfw - > card ,
snd_ctl_new1 ( & controls [ i ] , oxfw ) ) ;
if ( err < 0 )
return err ;
}
return 0 ;
}