sound: usb: Add helper APIs to enable audio stream
Adding helper API to find usb substream context information using card number, pcm device number and direction. This usb substream is used to enable/disable by issuing SET_ALT command to device. Also add disconnect call back to perform any clean up required. Change-Id: I252f5171bd94b5ab096eb1a2f053f29a8c049c3b Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
This commit is contained in:
parent
55cdf93234
commit
b27f238201
5 changed files with 133 additions and 1 deletions
|
@ -110,6 +110,71 @@ static DEFINE_MUTEX(register_mutex);
|
||||||
static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
|
static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
|
||||||
static struct usb_driver usb_audio_driver;
|
static struct usb_driver usb_audio_driver;
|
||||||
|
|
||||||
|
struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num,
|
||||||
|
unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio
|
||||||
|
**uchip, void (*disconnect_cb)(struct snd_usb_audio *chip))
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
struct snd_usb_stream *as;
|
||||||
|
struct snd_usb_substream *subs = NULL;
|
||||||
|
struct snd_usb_audio *chip = NULL;
|
||||||
|
|
||||||
|
mutex_lock(®ister_mutex);
|
||||||
|
/*
|
||||||
|
* legacy audio snd card number assignment is dynamic. Hence
|
||||||
|
* search using chip->card->number
|
||||||
|
*/
|
||||||
|
for (idx = 0; idx < SNDRV_CARDS; idx++) {
|
||||||
|
if (!usb_chip[idx])
|
||||||
|
continue;
|
||||||
|
if (usb_chip[idx]->card->number == card_num) {
|
||||||
|
chip = usb_chip[idx];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chip || atomic_read(&chip->shutdown)) {
|
||||||
|
pr_debug("%s: instance of usb crad # %d does not exist\n",
|
||||||
|
__func__, card_num);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pcm_idx >= chip->pcm_devs) {
|
||||||
|
pr_err("%s: invalid pcm dev number %u > %d\n", __func__,
|
||||||
|
pcm_idx, chip->pcm_devs);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (direction > SNDRV_PCM_STREAM_CAPTURE) {
|
||||||
|
pr_err("%s: invalid direction %u\n", __func__, direction);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(as, &chip->pcm_list, list) {
|
||||||
|
if (as->pcm_index == pcm_idx) {
|
||||||
|
subs = &as->substream[direction];
|
||||||
|
if (subs->interface < 0 && !subs->data_endpoint &&
|
||||||
|
!subs->sync_endpoint) {
|
||||||
|
pr_debug("%s: stream disconnected, bail out\n",
|
||||||
|
__func__);
|
||||||
|
subs = NULL;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
chip->card_num = card_num;
|
||||||
|
chip->disconnect_cb = disconnect_cb;
|
||||||
|
err:
|
||||||
|
*uchip = chip;
|
||||||
|
if (!subs)
|
||||||
|
pr_debug("%s: substream instance not found\n", __func__);
|
||||||
|
mutex_unlock(®ister_mutex);
|
||||||
|
return subs;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* disconnect streams
|
* disconnect streams
|
||||||
* called from usb_audio_disconnect()
|
* called from usb_audio_disconnect()
|
||||||
|
@ -639,6 +704,8 @@ static void usb_audio_disconnect(struct usb_interface *intf)
|
||||||
if (chip->num_interfaces <= 0) {
|
if (chip->num_interfaces <= 0) {
|
||||||
usb_chip[chip->index] = NULL;
|
usb_chip[chip->index] = NULL;
|
||||||
mutex_unlock(®ister_mutex);
|
mutex_unlock(®ister_mutex);
|
||||||
|
if (chip->disconnect_cb)
|
||||||
|
chip->disconnect_cb(chip);
|
||||||
snd_card_free_when_closed(card);
|
snd_card_free_when_closed(card);
|
||||||
} else {
|
} else {
|
||||||
mutex_unlock(®ister_mutex);
|
mutex_unlock(®ister_mutex);
|
||||||
|
|
|
@ -167,4 +167,8 @@ struct snd_usb_stream {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct snd_usb_substream *find_snd_usb_substream(unsigned int card_num,
|
||||||
|
unsigned int pcm_idx, unsigned int direction, struct snd_usb_audio
|
||||||
|
**uchip, void (*disconnect_cb)(struct snd_usb_audio *chip));
|
||||||
|
|
||||||
#endif /* __USBAUDIO_CARD_H */
|
#endif /* __USBAUDIO_CARD_H */
|
||||||
|
|
|
@ -552,6 +552,64 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
|
||||||
|
bool enable)
|
||||||
|
{
|
||||||
|
struct audioformat *fmt;
|
||||||
|
struct usb_host_interface *alts;
|
||||||
|
struct usb_interface *iface;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!enable) {
|
||||||
|
if (subs->interface >= 0) {
|
||||||
|
usb_set_interface(subs->dev, subs->interface, 0);
|
||||||
|
subs->altset_idx = 0;
|
||||||
|
subs->interface = -1;
|
||||||
|
subs->cur_audiofmt = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_usb_autosuspend(subs->stream->chip);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_usb_autoresume(subs->stream->chip);
|
||||||
|
fmt = find_format(subs);
|
||||||
|
if (!fmt) {
|
||||||
|
dev_dbg(&subs->dev->dev,
|
||||||
|
"cannot set format: format = %#x, rate = %d, channels = %d\n",
|
||||||
|
subs->pcm_format, subs->cur_rate, subs->channels);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
subs->altset_idx = 0;
|
||||||
|
subs->interface = -1;
|
||||||
|
if (atomic_read(&subs->stream->chip->shutdown)) {
|
||||||
|
ret = -ENODEV;
|
||||||
|
} else {
|
||||||
|
ret = set_format(subs, fmt);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
|
||||||
|
alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
|
||||||
|
ret = snd_usb_init_sample_rate(subs->stream->chip,
|
||||||
|
subs->cur_audiofmt->iface,
|
||||||
|
alts,
|
||||||
|
subs->cur_audiofmt,
|
||||||
|
subs->cur_rate);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(&subs->dev->dev, "failed to set rate %d\n",
|
||||||
|
subs->cur_rate);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
subs->interface = fmt->iface;
|
||||||
|
subs->altset_idx = fmt->altset_idx;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the score of matching two audioformats.
|
* Return the score of matching two audioformats.
|
||||||
* Veto the audioformat if:
|
* Veto the audioformat if:
|
||||||
|
|
|
@ -9,6 +9,7 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
|
||||||
int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
|
int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
|
||||||
struct usb_host_interface *alts,
|
struct usb_host_interface *alts,
|
||||||
struct audioformat *fmt);
|
struct audioformat *fmt);
|
||||||
|
int snd_usb_enable_audio_stream(struct snd_usb_substream *subs,
|
||||||
|
bool enable);
|
||||||
|
|
||||||
#endif /* __USBAUDIO_PCM_H */
|
#endif /* __USBAUDIO_PCM_H */
|
||||||
|
|
|
@ -60,6 +60,8 @@ struct snd_usb_audio {
|
||||||
bool autoclock; /* from the 'autoclock' module param */
|
bool autoclock; /* from the 'autoclock' module param */
|
||||||
|
|
||||||
struct usb_host_interface *ctrl_intf; /* the audio control interface */
|
struct usb_host_interface *ctrl_intf; /* the audio control interface */
|
||||||
|
int card_num; /* cache pcm card number to use upon disconnect */
|
||||||
|
void (*disconnect_cb)(struct snd_usb_audio *chip);
|
||||||
};
|
};
|
||||||
|
|
||||||
#define usb_audio_err(chip, fmt, args...) \
|
#define usb_audio_err(chip, fmt, args...) \
|
||||||
|
|
Loading…
Add table
Reference in a new issue