ANDROID: sound: usb: Add vendor's hooking interface
In mobile, a co-processor can be used with USB audio to improve power consumption. To support this type of hardware, hooks need to be added to the USB audio subsystem to be able to call into the hardware when needed. This interface can be support multiful USB audio devices. It is depends on co-processor's F/W. The main operation of the call-backs are: - Initialize the co-processor by transmitting data when initializing. - Change the co-processor setting value through the interface function. - Configure sampling rate - pcm open/close - other housekeeping Bug: 156315379 Signed-off-by: Oh Eomji <eomji.oh@samsung.com> Signed-off-by: JaeHun Jung <jh0801.jung@samsung.com> [rework api to be a bit more self-contained and obvious - gregkh] Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I39061f6cc85be7bcae8db0e612fe58396bdedb24
This commit is contained in:
parent
2c6f80378c
commit
a7cd7a3dd7
@ -119,6 +119,56 @@ MODULE_PARM_DESC(skip_validation, "Skip unit descriptor validation (default: no)
|
||||
static DEFINE_MUTEX(register_mutex);
|
||||
static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
|
||||
static struct usb_driver usb_audio_driver;
|
||||
static struct snd_usb_audio_vendor_ops *usb_vendor_ops;
|
||||
|
||||
int snd_vendor_set_ops(struct snd_usb_audio_vendor_ops *ops)
|
||||
{
|
||||
if ((!ops->set_interface) ||
|
||||
(!ops->set_pcm_intf) ||
|
||||
(!ops->set_pcm_connection))
|
||||
return -EINVAL;
|
||||
|
||||
usb_vendor_ops = ops;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_vendor_set_ops);
|
||||
|
||||
struct snd_usb_audio_vendor_ops *snd_vendor_get_ops(void)
|
||||
{
|
||||
return usb_vendor_ops;
|
||||
}
|
||||
|
||||
int snd_vendor_set_interface(struct usb_device *udev,
|
||||
struct usb_host_interface *intf,
|
||||
int iface, int alt)
|
||||
{
|
||||
struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops();
|
||||
|
||||
if (ops)
|
||||
return ops->set_interface(udev, intf, iface, alt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_vendor_set_pcm_intf(struct usb_interface *intf, int iface, int alt,
|
||||
int direction, struct snd_usb_substream *subs)
|
||||
{
|
||||
struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops();
|
||||
|
||||
if (ops)
|
||||
return ops->set_pcm_intf(intf, iface, alt, direction, subs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_vendor_set_pcm_connection(struct usb_device *udev,
|
||||
enum snd_vendor_pcm_open_close onoff,
|
||||
int direction)
|
||||
{
|
||||
struct snd_usb_audio_vendor_ops *ops = snd_vendor_get_ops();
|
||||
|
||||
if (ops)
|
||||
return ops->set_pcm_connection(udev, onoff, direction);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* disconnect streams
|
||||
|
@ -216,4 +216,19 @@ struct snd_usb_stream {
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
int snd_vendor_set_ops(struct snd_usb_audio_vendor_ops *vendor_ops);
|
||||
struct snd_usb_audio_vendor_ops *snd_vendor_get_ops(void);
|
||||
int snd_vendor_set_interface(struct usb_device *udev,
|
||||
struct usb_host_interface *alts,
|
||||
int iface, int alt);
|
||||
int snd_vendor_set_rate(int iface, int rate, int alt);
|
||||
int snd_vendor_set_pcm_intf(struct usb_interface *intf, int iface, int alt,
|
||||
int direction, struct snd_usb_substream *subs);
|
||||
int snd_vendor_set_pcm_connection(struct usb_device *udev,
|
||||
enum snd_vendor_pcm_open_close onoff,
|
||||
int direction);
|
||||
int snd_vendor_set_pcm_binterval(const struct audioformat *fp,
|
||||
const struct audioformat *found,
|
||||
int *cur_attr, int *attr);
|
||||
|
||||
#endif /* __USBAUDIO_CARD_H */
|
||||
|
@ -641,6 +641,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
struct snd_usb_audio *chip = subs->stream->chip;
|
||||
int retry = 0;
|
||||
int ret;
|
||||
struct usb_interface *iface;
|
||||
|
||||
ret = snd_usb_lock_shutdown(chip);
|
||||
if (ret < 0)
|
||||
@ -653,6 +654,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
again:
|
||||
if (subs->sync_endpoint) {
|
||||
ret = snd_usb_endpoint_prepare(chip, subs->sync_endpoint);
|
||||
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
}
|
||||
@ -664,6 +666,14 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
|
||||
snd_usb_set_format_quirk(subs, subs->cur_audiofmt);
|
||||
ret = 0;
|
||||
|
||||
iface = usb_ifnum_to_if(chip->dev, subs->data_endpoint->iface);
|
||||
|
||||
ret = snd_vendor_set_pcm_intf(iface, subs->data_endpoint->iface,
|
||||
subs->data_endpoint->altsetting,
|
||||
subs->direction, subs);
|
||||
if (!ret)
|
||||
goto unlock;
|
||||
|
||||
/* reset the pointer */
|
||||
subs->buffer_bytes = frames_to_bytes(runtime, runtime->buffer_size);
|
||||
subs->inflight_bytes = 0;
|
||||
@ -1162,6 +1172,11 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
|
||||
struct snd_usb_substream *subs = &as->substream[direction];
|
||||
int ret;
|
||||
|
||||
ret = snd_vendor_set_pcm_connection(subs->dev, SOUND_PCM_OPEN,
|
||||
direction);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
runtime->hw = snd_usb_hardware;
|
||||
/* need an explicit sync to catch applptr update in low-latency mode */
|
||||
if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
|
||||
@ -1195,6 +1210,11 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream)
|
||||
struct snd_usb_substream *subs = &as->substream[direction];
|
||||
int ret;
|
||||
|
||||
ret = snd_vendor_set_pcm_connection(subs->dev, SOUND_PCM_CLOSE,
|
||||
direction);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
snd_media_stop_pipeline(subs);
|
||||
|
||||
if (!snd_usb_lock_shutdown(subs->stream->chip)) {
|
||||
|
@ -1228,6 +1228,8 @@ static int __snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
|
||||
snd_usb_init_pitch(chip, fp);
|
||||
snd_usb_init_sample_rate(chip, fp, fp->rate_max);
|
||||
usb_set_interface(chip->dev, iface_no, altno);
|
||||
if (protocol > UAC_VERSION_1)
|
||||
snd_vendor_set_interface(chip->dev, alts, iface_no, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
struct media_device;
|
||||
struct media_intf_devnode;
|
||||
struct snd_usb_substream;
|
||||
|
||||
#define MAX_CARD_INTERFACES 16
|
||||
|
||||
@ -210,4 +211,37 @@ extern bool snd_usb_skip_validation;
|
||||
#define QUIRK_FLAG_FORCE_IFACE_RESET (1U << 20)
|
||||
#define QUIRK_FLAG_FIXED_RATE (1U << 21)
|
||||
|
||||
struct audioformat;
|
||||
|
||||
enum snd_vendor_pcm_open_close {
|
||||
SOUND_PCM_CLOSE = 0,
|
||||
SOUND_PCM_OPEN,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct snd_usb_audio_vendor_ops - function callbacks for USB audio accelerators
|
||||
* @set_interface: called when an interface is initialized
|
||||
* @set_pcm_intf: called when the pcm interface is set
|
||||
* @set_pcm_connection: called when pcm is opened/closed
|
||||
*
|
||||
* Set of callbacks for some accelerated USB audio streaming hardware.
|
||||
*
|
||||
* TODO: make this USB host-controller specific, right now this only works for
|
||||
* one USB controller in the system at a time, which is only realistic for
|
||||
* self-contained systems like phones.
|
||||
*/
|
||||
struct snd_usb_audio_vendor_ops {
|
||||
int (*set_interface)(struct usb_device *udev,
|
||||
struct usb_host_interface *alts,
|
||||
int iface, int alt);
|
||||
int (*set_pcm_intf)(struct usb_interface *intf, int iface, int alt,
|
||||
int direction, struct snd_usb_substream *subs);
|
||||
int (*set_pcm_connection)(struct usb_device *udev,
|
||||
enum snd_vendor_pcm_open_close onoff,
|
||||
int direction);
|
||||
ANDROID_KABI_RESERVE(1);
|
||||
ANDROID_KABI_RESERVE(2);
|
||||
ANDROID_KABI_RESERVE(3);
|
||||
ANDROID_KABI_RESERVE(4);
|
||||
};
|
||||
#endif /* __USBAUDIO_H */
|
||||
|
Loading…
Reference in New Issue
Block a user