From a7cd7a3dd770bf7e467746de5343a6c0f8656d19 Mon Sep 17 00:00:00 2001 From: JaeHun Jung Date: Tue, 26 May 2020 16:53:38 +0900 Subject: [PATCH] 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 Signed-off-by: JaeHun Jung [rework api to be a bit more self-contained and obvious - gregkh] Signed-off-by: Greg Kroah-Hartman Change-Id: I39061f6cc85be7bcae8db0e612fe58396bdedb24 --- sound/usb/card.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ sound/usb/card.h | 15 +++++++++++++ sound/usb/pcm.c | 20 ++++++++++++++++++ sound/usb/stream.c | 2 ++ sound/usb/usbaudio.h | 34 ++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+) diff --git a/sound/usb/card.c b/sound/usb/card.c index 4e535eeddd43..96d8e1742af0 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -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 diff --git a/sound/usb/card.h b/sound/usb/card.h index e9b6afd2275a..5421f9e53c54 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -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 */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 1e1d7458bce1..cc0903af6a7f 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -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)) { diff --git a/sound/usb/stream.c b/sound/usb/stream.c index f10f4e6d3fb8..dbe86811346f 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -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; } diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 1b463ce52324..b2cafc0b5eef 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -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 */