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:
JaeHun Jung 2020-05-26 16:53:38 +09:00 committed by Carlos Llamas
parent 2c6f80378c
commit a7cd7a3dd7
5 changed files with 121 additions and 0 deletions

View File

@ -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

View File

@ -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 */

View File

@ -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)) {

View File

@ -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;
}

View File

@ -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 */