2020-11-23 09:53:43 +01:00
// SPDX-License-Identifier: GPL-2.0-or-later
//
// Special handling for implicit feedback mode
//
# include <linux/init.h>
# include <linux/usb.h>
# include <linux/usb/audio.h>
# include <linux/usb/audio-v2.h>
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
# include "usbaudio.h"
# include "card.h"
# include "helper.h"
# include "implicit.h"
enum {
IMPLICIT_FB_NONE ,
2020-11-23 09:53:44 +01:00
IMPLICIT_FB_GENERIC ,
2020-11-23 09:53:43 +01:00
IMPLICIT_FB_FIXED ,
} ;
struct snd_usb_implicit_fb_match {
unsigned int id ;
unsigned int iface_class ;
unsigned int ep_num ;
unsigned int iface ;
int type ;
} ;
2020-11-23 09:53:44 +01:00
# define IMPLICIT_FB_GENERIC_DEV(vend, prod) \
{ . id = USB_ID ( vend , prod ) , . type = IMPLICIT_FB_GENERIC }
2020-11-23 09:53:43 +01:00
# define IMPLICIT_FB_FIXED_DEV(vend, prod, ep, ifnum) \
{ . id = USB_ID ( vend , prod ) , . type = IMPLICIT_FB_FIXED , . ep_num = ( ep ) , \
. iface = ( ifnum ) }
# define IMPLICIT_FB_SKIP_DEV(vend, prod) \
{ . id = USB_ID ( vend , prod ) , . type = IMPLICIT_FB_NONE }
/* Implicit feedback quirk table for playback */
static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks [ ] = {
2020-11-23 09:53:44 +01:00
/* Generic matching */
IMPLICIT_FB_GENERIC_DEV ( 0x0763 , 0x2080 ) , /* M-Audio FastTrack Ultra */
IMPLICIT_FB_GENERIC_DEV ( 0x0763 , 0x2081 ) , /* M-Audio FastTrack Ultra */
IMPLICIT_FB_GENERIC_DEV ( 0x0763 , 0x2030 ) , /* M-Audio Fast Track C400 */
IMPLICIT_FB_GENERIC_DEV ( 0x0763 , 0x2031 ) , /* M-Audio Fast Track C600 */
2020-11-23 09:53:43 +01:00
/* Fixed EP */
2020-11-23 09:53:44 +01:00
/* FIXME: check the availability of generic matching */
2020-11-23 09:53:43 +01:00
IMPLICIT_FB_FIXED_DEV ( 0x1397 , 0x0001 , 0x81 , 1 ) , /* Behringer UFX1604 */
IMPLICIT_FB_FIXED_DEV ( 0x1397 , 0x0002 , 0x81 , 1 ) , /* Behringer UFX1204 */
IMPLICIT_FB_FIXED_DEV ( 0x2466 , 0x8010 , 0x81 , 2 ) , /* Fractal Audio Axe-Fx III */
IMPLICIT_FB_FIXED_DEV ( 0x31e9 , 0x0001 , 0x81 , 2 ) , /* Solid State Logic SSL2 */
IMPLICIT_FB_FIXED_DEV ( 0x31e9 , 0x0002 , 0x81 , 2 ) , /* Solid State Logic SSL2+ */
IMPLICIT_FB_FIXED_DEV ( 0x0499 , 0x172f , 0x81 , 2 ) , /* Steinberg UR22C */
IMPLICIT_FB_FIXED_DEV ( 0x0d9a , 0x00df , 0x81 , 2 ) , /* RTX6001 */
IMPLICIT_FB_FIXED_DEV ( 0x22f0 , 0x0006 , 0x81 , 3 ) , /* Allen&Heath Qu-16 */
IMPLICIT_FB_FIXED_DEV ( 0x2b73 , 0x000a , 0x82 , 0 ) , /* Pioneer DJ DJM-900NXS2 */
IMPLICIT_FB_FIXED_DEV ( 0x2b73 , 0x0017 , 0x82 , 0 ) , /* Pioneer DJ DJM-250MK2 */
IMPLICIT_FB_FIXED_DEV ( 0x1686 , 0xf029 , 0x82 , 2 ) , /* Zoom UAC-2 */
IMPLICIT_FB_FIXED_DEV ( 0x2466 , 0x8003 , 0x86 , 2 ) , /* Fractal Audio Axe-Fx II */
IMPLICIT_FB_FIXED_DEV ( 0x0499 , 0x172a , 0x86 , 2 ) , /* Yamaha MODX */
/* Special matching */
{ . id = USB_ID ( 0x07fd , 0x0004 ) , . iface_class = USB_CLASS_AUDIO ,
. type = IMPLICIT_FB_NONE } , /* MicroBook IIc */
/* ep = 0x84, ifnum = 0 */
{ . id = USB_ID ( 0x07fd , 0x0004 ) , . iface_class = USB_CLASS_VENDOR_SPEC ,
. type = IMPLICIT_FB_FIXED ,
. ep_num = 0x84 , . iface = 0 } , /* MOTU MicroBook II */
/* No quirk for playback but with capture quirk (see below) */
2020-11-23 09:53:46 +01:00
IMPLICIT_FB_SKIP_DEV ( 0x0582 , 0x0130 ) , /* BOSS BR-80 */
IMPLICIT_FB_SKIP_DEV ( 0x0582 , 0x0189 ) , /* BOSS GT-100v2 */
IMPLICIT_FB_SKIP_DEV ( 0x0582 , 0x01d8 ) , /* BOSS Katana */
2020-11-23 09:53:43 +01:00
IMPLICIT_FB_SKIP_DEV ( 0x0582 , 0x01e5 ) , /* BOSS GT-001 */
{ } /* terminator */
} ;
2020-11-23 09:53:44 +01:00
/* Implicit feedback quirk table for capture: only FIXED type */
2020-11-23 09:53:43 +01:00
static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks [ ] = {
2020-11-23 09:53:46 +01:00
IMPLICIT_FB_FIXED_DEV ( 0x0582 , 0x0130 , 0x0d , 0x01 ) , /* BOSS BR-80 */
IMPLICIT_FB_FIXED_DEV ( 0x0582 , 0x0189 , 0x0d , 0x01 ) , /* BOSS GT-100v2 */
IMPLICIT_FB_FIXED_DEV ( 0x0582 , 0x01d8 , 0x0d , 0x01 ) , /* BOSS Katana */
2020-11-23 09:53:43 +01:00
IMPLICIT_FB_FIXED_DEV ( 0x0582 , 0x01e5 , 0x0d , 0x01 ) , /* BOSS GT-001 */
{ } /* terminator */
} ;
/* set up sync EP information on the audioformat */
static int add_implicit_fb_sync_ep ( struct snd_usb_audio * chip ,
struct audioformat * fmt ,
int ep , int ifnum ,
const struct usb_host_interface * alts )
{
struct usb_interface * iface ;
if ( ! alts ) {
iface = usb_ifnum_to_if ( chip - > dev , ifnum ) ;
if ( ! iface | | iface - > num_altsetting < 2 )
return 0 ;
alts = & iface - > altsetting [ 1 ] ;
}
fmt - > sync_ep = ep ;
fmt - > sync_iface = ifnum ;
fmt - > sync_altsetting = alts - > desc . bAlternateSetting ;
fmt - > sync_ep_idx = 0 ;
fmt - > implicit_fb = 1 ;
usb_audio_dbg ( chip ,
" %d:%d: added %s implicit_fb sync_ep %x, iface %d:%d \n " ,
fmt - > iface , fmt - > altsetting ,
( ep & USB_DIR_IN ) ? " playback " : " capture " ,
fmt - > sync_ep , fmt - > sync_iface , fmt - > sync_altsetting ) ;
return 1 ;
}
/* Check whether the given UAC2 iface:altset points to an implicit fb source */
static int add_generic_uac2_implicit_fb ( struct snd_usb_audio * chip ,
struct audioformat * fmt ,
unsigned int ifnum ,
unsigned int altsetting )
{
struct usb_host_interface * alts ;
struct usb_endpoint_descriptor * epd ;
alts = snd_usb_get_host_interface ( chip , ifnum , altsetting ) ;
if ( ! alts )
return 0 ;
if ( alts - > desc . bInterfaceClass ! = USB_CLASS_AUDIO | |
alts - > desc . bInterfaceSubClass ! = USB_SUBCLASS_AUDIOSTREAMING | |
alts - > desc . bInterfaceProtocol ! = UAC_VERSION_2 | |
alts - > desc . bNumEndpoints < 1 )
return 0 ;
epd = get_endpoint ( alts , 0 ) ;
if ( ! usb_endpoint_is_isoc_in ( epd ) | |
( epd - > bmAttributes & USB_ENDPOINT_USAGE_MASK ) ! =
USB_ENDPOINT_USAGE_IMPLICIT_FB )
return 0 ;
return add_implicit_fb_sync_ep ( chip , fmt , epd - > bEndpointAddress ,
ifnum , alts ) ;
}
/* Like the function above, but specific to Roland with vendor class and hack */
static int add_roland_implicit_fb ( struct snd_usb_audio * chip ,
struct audioformat * fmt ,
unsigned int ifnum ,
unsigned int altsetting )
{
struct usb_host_interface * alts ;
struct usb_endpoint_descriptor * epd ;
alts = snd_usb_get_host_interface ( chip , ifnum , altsetting ) ;
if ( ! alts )
return 0 ;
if ( alts - > desc . bInterfaceClass ! = USB_CLASS_VENDOR_SPEC | |
( alts - > desc . bInterfaceSubClass ! = 2 & &
alts - > desc . bInterfaceProtocol ! = 2 ) | |
alts - > desc . bNumEndpoints < 1 )
return 0 ;
epd = get_endpoint ( alts , 0 ) ;
if ( ! usb_endpoint_is_isoc_in ( epd ) | |
( epd - > bmAttributes & USB_ENDPOINT_USAGE_MASK ) ! =
USB_ENDPOINT_USAGE_IMPLICIT_FB )
return 0 ;
return add_implicit_fb_sync_ep ( chip , fmt , epd - > bEndpointAddress ,
ifnum , alts ) ;
}
2020-11-23 09:53:44 +01:00
static int __add_generic_implicit_fb ( struct snd_usb_audio * chip ,
struct audioformat * fmt ,
int iface , int altset )
{
struct usb_host_interface * alts ;
struct usb_endpoint_descriptor * epd ;
alts = snd_usb_get_host_interface ( chip , iface , altset ) ;
if ( ! alts )
return 0 ;
if ( ( alts - > desc . bInterfaceClass ! = USB_CLASS_VENDOR_SPEC & &
alts - > desc . bInterfaceClass ! = USB_CLASS_AUDIO ) | |
alts - > desc . bNumEndpoints < 1 )
return 0 ;
epd = get_endpoint ( alts , 0 ) ;
if ( ! usb_endpoint_is_isoc_in ( epd ) | |
( epd - > bmAttributes & USB_ENDPOINT_SYNCTYPE ) ! = USB_ENDPOINT_SYNC_ASYNC )
return 0 ;
return add_implicit_fb_sync_ep ( chip , fmt , epd - > bEndpointAddress ,
iface , alts ) ;
}
/* More generic quirk: look for the sync EP next to the data EP */
static int add_generic_implicit_fb ( struct snd_usb_audio * chip ,
struct audioformat * fmt ,
struct usb_host_interface * alts )
{
if ( ( fmt - > ep_attr & USB_ENDPOINT_SYNCTYPE ) ! = USB_ENDPOINT_SYNC_ASYNC )
return 0 ;
if ( __add_generic_implicit_fb ( chip , fmt ,
alts - > desc . bInterfaceNumber + 1 ,
alts - > desc . bAlternateSetting ) )
return 1 ;
return __add_generic_implicit_fb ( chip , fmt ,
alts - > desc . bInterfaceNumber - 1 ,
alts - > desc . bAlternateSetting ) ;
}
2020-11-23 09:53:43 +01:00
static const struct snd_usb_implicit_fb_match *
find_implicit_fb_entry ( struct snd_usb_audio * chip ,
const struct snd_usb_implicit_fb_match * match ,
const struct usb_host_interface * alts )
{
for ( ; match - > id ; match + + )
if ( match - > id = = chip - > usb_id & &
( ! match - > iface_class | |
( alts - > desc . bInterfaceClass = = match - > iface_class ) ) )
return match ;
return NULL ;
}
/* Setup an implicit feedback endpoint from a quirk. Returns 0 if no quirk
* applies . Returns 1 if a quirk was found .
*/
static int audioformat_implicit_fb_quirk ( struct snd_usb_audio * chip ,
struct audioformat * fmt ,
struct usb_host_interface * alts )
{
const struct snd_usb_implicit_fb_match * p ;
unsigned int attr = fmt - > ep_attr & USB_ENDPOINT_SYNCTYPE ;
p = find_implicit_fb_entry ( chip , playback_implicit_fb_quirks , alts ) ;
if ( p ) {
switch ( p - > type ) {
2020-11-23 09:53:44 +01:00
case IMPLICIT_FB_GENERIC :
return add_generic_implicit_fb ( chip , fmt , alts ) ;
2020-11-23 09:53:43 +01:00
case IMPLICIT_FB_NONE :
return 0 ; /* No quirk */
case IMPLICIT_FB_FIXED :
return add_implicit_fb_sync_ep ( chip , fmt , p - > ep_num ,
p - > iface , NULL ) ;
}
}
/* Generic UAC2 implicit feedback */
if ( attr = = USB_ENDPOINT_SYNC_ASYNC & &
alts - > desc . bInterfaceClass = = USB_CLASS_AUDIO & &
alts - > desc . bInterfaceProtocol = = UAC_VERSION_2 & &
alts - > desc . bNumEndpoints = = 1 ) {
if ( add_generic_uac2_implicit_fb ( chip , fmt ,
alts - > desc . bInterfaceNumber + 1 ,
alts - > desc . bAlternateSetting ) )
return 1 ;
}
/* Roland/BOSS implicit feedback with vendor spec class */
if ( attr = = USB_ENDPOINT_SYNC_ASYNC & &
alts - > desc . bInterfaceClass = = USB_CLASS_VENDOR_SPEC & &
alts - > desc . bInterfaceProtocol = = 2 & &
alts - > desc . bNumEndpoints = = 1 & &
USB_ID_VENDOR ( chip - > usb_id ) = = 0x0582 /* Roland */ ) {
if ( add_roland_implicit_fb ( chip , fmt ,
alts - > desc . bInterfaceNumber + 1 ,
alts - > desc . bAlternateSetting ) )
return 1 ;
}
2020-11-23 09:53:45 +01:00
/* Try the generic implicit fb if available */
if ( chip - > generic_implicit_fb )
return add_generic_implicit_fb ( chip , fmt , alts ) ;
2020-11-23 09:53:43 +01:00
/* No quirk */
return 0 ;
}
/* same for capture, but only handling FIXED entry */
static int audioformat_capture_quirk ( struct snd_usb_audio * chip ,
struct audioformat * fmt ,
struct usb_host_interface * alts )
{
const struct snd_usb_implicit_fb_match * p ;
p = find_implicit_fb_entry ( chip , capture_implicit_fb_quirks , alts ) ;
if ( p & & p - > type = = IMPLICIT_FB_FIXED )
return add_implicit_fb_sync_ep ( chip , fmt , p - > ep_num , p - > iface ,
NULL ) ;
return 0 ;
}
/*
* Parse altset and set up implicit feedback endpoint on the audioformat
*/
int snd_usb_parse_implicit_fb_quirk ( struct snd_usb_audio * chip ,
struct audioformat * fmt ,
struct usb_host_interface * alts )
{
if ( fmt - > endpoint & USB_DIR_IN )
return audioformat_capture_quirk ( chip , fmt , alts ) ;
else
return audioformat_implicit_fb_quirk ( chip , fmt , alts ) ;
}
/*
* Return the score of matching two audioformats .
* Veto the audioformat if :
* - It has no channels for some reason .
* - Requested PCM format is not supported .
* - Requested sample rate is not supported .
*/
static int match_endpoint_audioformats ( struct snd_usb_substream * subs ,
const struct audioformat * fp ,
int rate , int channels ,
snd_pcm_format_t pcm_format )
{
int i , score ;
if ( fp - > channels < 1 )
return 0 ;
if ( ! ( fp - > formats & pcm_format_to_bits ( pcm_format ) ) )
return 0 ;
if ( fp - > rates & SNDRV_PCM_RATE_CONTINUOUS ) {
if ( rate < fp - > rate_min | | rate > fp - > rate_max )
return 0 ;
} else {
for ( i = 0 ; i < fp - > nr_rates ; i + + ) {
if ( fp - > rate_table [ i ] = = rate )
break ;
}
if ( i > = fp - > nr_rates )
return 0 ;
}
score = 1 ;
if ( fp - > channels = = channels )
score + + ;
return score ;
}
static struct snd_usb_substream *
find_matching_substream ( struct snd_usb_audio * chip , int stream , int ep_num ,
int fmt_type )
{
struct snd_usb_stream * as ;
struct snd_usb_substream * subs ;
list_for_each_entry ( as , & chip - > pcm_list , list ) {
subs = & as - > substream [ stream ] ;
if ( as - > fmt_type = = fmt_type & & subs - > ep_num = = ep_num )
return subs ;
}
return NULL ;
}
/*
* Return the audioformat that is suitable for the implicit fb
*/
const struct audioformat *
snd_usb_find_implicit_fb_sync_format ( struct snd_usb_audio * chip ,
const struct audioformat * target ,
const struct snd_pcm_hw_params * params ,
int stream )
{
struct snd_usb_substream * subs ;
const struct audioformat * fp , * sync_fmt ;
int score , high_score ;
/* When sharing the same altset, use the original audioformat */
if ( target - > iface = = target - > sync_iface & &
target - > altsetting = = target - > sync_altsetting )
return target ;
subs = find_matching_substream ( chip , stream , target - > sync_ep ,
target - > fmt_type ) ;
if ( ! subs )
return NULL ;
sync_fmt = NULL ;
high_score = 0 ;
list_for_each_entry ( fp , & subs - > fmt_list , list ) {
score = match_endpoint_audioformats ( subs , fp ,
params_rate ( params ) ,
params_channels ( params ) ,
params_format ( params ) ) ;
if ( score > high_score ) {
sync_fmt = fp ;
high_score = score ;
}
}
return sync_fmt ;
}