[media] em28xx: Add support for devices with a separate audio interface
Some devices use a separate interface for the vendor audio class. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
parent
dff0f8c279
commit
4f83e7b3ef
|
@ -3,9 +3,9 @@
|
|||
*
|
||||
* Copyright (C) 2006 Markus Rechberger <mrechberger@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@infradead.org>
|
||||
* Copyright (C) 2007-2011 Mauro Carvalho Chehab <mchehab@redhat.com>
|
||||
* - Port to work with the in-kernel driver
|
||||
* - Several cleanups
|
||||
* - Cleanups, fixes, alsa-controls, etc.
|
||||
*
|
||||
* This driver is based on my previous au600 usb pstn audio driver
|
||||
* and inherits all the copyrights
|
||||
|
@ -281,23 +281,27 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Sets volume, mute, etc */
|
||||
|
||||
dev->mute = 0;
|
||||
mutex_lock(&dev->lock);
|
||||
ret = em28xx_audio_analog_set(dev);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
runtime->hw = snd_em28xx_hw_capture;
|
||||
if (dev->alt == 0 && dev->adev.users == 0) {
|
||||
dev->alt = 7;
|
||||
dprintk("changing alternate number to 7\n");
|
||||
usb_set_interface(dev->udev, 0, 7);
|
||||
}
|
||||
if ((dev->alt == 0 || dev->audio_ifnum) && dev->adev.users == 0) {
|
||||
if (dev->audio_ifnum)
|
||||
dev->alt = 1;
|
||||
else
|
||||
dev->alt = 7;
|
||||
|
||||
dev->adev.users++;
|
||||
mutex_unlock(&dev->lock);
|
||||
dprintk("changing alternate number on interface %d to %d\n",
|
||||
dev->audio_ifnum, dev->alt);
|
||||
usb_set_interface(dev->udev, dev->audio_ifnum, dev->alt);
|
||||
|
||||
/* Sets volume, mute, etc */
|
||||
dev->mute = 0;
|
||||
mutex_lock(&dev->lock);
|
||||
ret = em28xx_audio_analog_set(dev);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
dev->adev.users++;
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
|
||||
dev->adev.capture_pcm_substream = substream;
|
||||
|
@ -635,17 +639,17 @@ static int em28xx_audio_init(struct em28xx *dev)
|
|||
static int devnr;
|
||||
int err;
|
||||
|
||||
if (dev->has_alsa_audio != 1) {
|
||||
if (!dev->has_alsa_audio || dev->audio_ifnum < 0) {
|
||||
/* This device does not support the extension (in this case
|
||||
the device is expecting the snd-usb-audio module or
|
||||
doesn't have analog audio support at all) */
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "em28xx-audio.c: probing for em28x1 "
|
||||
"non standard usbaudio\n");
|
||||
printk(KERN_INFO "em28xx-audio.c: probing for em28xx Audio Vendor Class\n");
|
||||
printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
|
||||
"Rechberger\n");
|
||||
printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2007-2011 Mauro Carvalho Chehab\n");
|
||||
|
||||
err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0,
|
||||
&card);
|
||||
|
@ -737,7 +741,7 @@ static void __exit em28xx_alsa_unregister(void)
|
|||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Markus Rechberger <mrechberger@gmail.com>");
|
||||
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
|
||||
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
|
||||
MODULE_DESCRIPTION("Em28xx Audio driver");
|
||||
|
||||
module_init(em28xx_alsa_register);
|
||||
|
|
|
@ -2846,6 +2846,16 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
|
|||
}
|
||||
}
|
||||
|
||||
if (dev->is_audio_only) {
|
||||
errCode = em28xx_audio_setup(dev);
|
||||
if (errCode)
|
||||
return -ENODEV;
|
||||
em28xx_add_into_devlist(dev);
|
||||
em28xx_init_extension(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prepopulate cached GPO register content */
|
||||
retval = em28xx_read_reg(dev, dev->reg_gpo_num);
|
||||
if (retval >= 0)
|
||||
|
@ -2946,6 +2956,9 @@ fail_reg_devices:
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */
|
||||
#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
|
||||
|
||||
/*
|
||||
* em28xx_usb_probe()
|
||||
* checks for supported devices
|
||||
|
@ -2955,15 +2968,15 @@ static int em28xx_usb_probe(struct usb_interface *interface,
|
|||
{
|
||||
const struct usb_endpoint_descriptor *endpoint;
|
||||
struct usb_device *udev;
|
||||
struct usb_interface *uif;
|
||||
struct em28xx *dev = NULL;
|
||||
int retval;
|
||||
int i, nr, ifnum, isoc_pipe;
|
||||
bool is_audio_only = false, has_audio = false;
|
||||
int i, nr, isoc_pipe;
|
||||
const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
|
||||
char *speed;
|
||||
char descr[255] = "";
|
||||
|
||||
udev = usb_get_dev(interface_to_usbdev(interface));
|
||||
ifnum = interface->altsetting[0].desc.bInterfaceNumber;
|
||||
|
||||
/* Check to see next free device and mark as used */
|
||||
nr = find_first_zero_bit(&em28xx_devused, EM28XX_MAXBOARDS);
|
||||
|
@ -2983,6 +2996,19 @@ static int em28xx_usb_probe(struct usb_interface *interface,
|
|||
goto err;
|
||||
}
|
||||
|
||||
/* Get endpoints */
|
||||
for (i = 0; i < interface->num_altsetting; i++) {
|
||||
int ep;
|
||||
|
||||
for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) {
|
||||
struct usb_host_endpoint *e;
|
||||
e = &interface->altsetting[i].endpoint[ep];
|
||||
|
||||
if (e->desc.bEndpointAddress == 0x83)
|
||||
has_audio = true;
|
||||
}
|
||||
}
|
||||
|
||||
endpoint = &interface->cur_altsetting->endpoint[0].desc;
|
||||
|
||||
/* check if the device has the iso in endpoint at the correct place */
|
||||
|
@ -3002,19 +3028,22 @@ static int em28xx_usb_probe(struct usb_interface *interface,
|
|||
check_interface = 0;
|
||||
|
||||
if (!check_interface) {
|
||||
em28xx_err(DRIVER_NAME " video device (%04x:%04x): "
|
||||
"interface %i, class %i found.\n",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct),
|
||||
ifnum,
|
||||
interface->altsetting[0].desc.bInterfaceClass);
|
||||
if (has_audio) {
|
||||
is_audio_only = true;
|
||||
} else {
|
||||
em28xx_err(DRIVER_NAME " video device (%04x:%04x): "
|
||||
"interface %i, class %i found.\n",
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
le16_to_cpu(udev->descriptor.idProduct),
|
||||
ifnum,
|
||||
interface->altsetting[0].desc.bInterfaceClass);
|
||||
em28xx_err(DRIVER_NAME " This is an anciliary "
|
||||
"interface not used by the driver\n");
|
||||
|
||||
em28xx_err(DRIVER_NAME " This is an anciliary "
|
||||
"interface not used by the driver\n");
|
||||
|
||||
em28xx_devused &= ~(1<<nr);
|
||||
retval = -ENODEV;
|
||||
goto err;
|
||||
em28xx_devused &= ~(1<<nr);
|
||||
retval = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3044,8 +3073,8 @@ static int em28xx_usb_probe(struct usb_interface *interface,
|
|||
if (*descr)
|
||||
strlcat(descr, " ", sizeof(descr));
|
||||
|
||||
printk(DRIVER_NAME ": New device %s@ %s Mbps "
|
||||
"(%04x:%04x, interface %d, class %d)\n",
|
||||
printk(KERN_INFO DRIVER_NAME
|
||||
": New device %s@ %s Mbps (%04x:%04x, interface %d, class %d)\n",
|
||||
descr,
|
||||
speed,
|
||||
le16_to_cpu(udev->descriptor.idVendor),
|
||||
|
@ -3053,6 +3082,11 @@ static int em28xx_usb_probe(struct usb_interface *interface,
|
|||
ifnum,
|
||||
interface->altsetting->desc.bInterfaceNumber);
|
||||
|
||||
if (has_audio)
|
||||
printk(KERN_INFO DRIVER_NAME
|
||||
": Audio Vendor Class interface %i found\n",
|
||||
ifnum);
|
||||
|
||||
/*
|
||||
* Make sure we have 480 Mbps of bandwidth, otherwise things like
|
||||
* video stream wouldn't likely work, since 12 Mbps is generally
|
||||
|
@ -3088,10 +3122,13 @@ static int em28xx_usb_probe(struct usb_interface *interface,
|
|||
dev->devno = nr;
|
||||
dev->model = id->driver_info;
|
||||
dev->alt = -1;
|
||||
dev->is_audio_only = is_audio_only;
|
||||
dev->has_alsa_audio = has_audio;
|
||||
dev->audio_ifnum = ifnum;
|
||||
|
||||
/* Checks if audio is provided by some interface */
|
||||
for (i = 0; i < udev->config->desc.bNumInterfaces; i++) {
|
||||
uif = udev->config->interface[i];
|
||||
struct usb_interface *uif = udev->config->interface[i];
|
||||
if (uif->altsetting[0].desc.bInterfaceClass == USB_CLASS_AUDIO) {
|
||||
dev->has_audio_class = 1;
|
||||
break;
|
||||
|
@ -3099,9 +3136,7 @@ static int em28xx_usb_probe(struct usb_interface *interface,
|
|||
}
|
||||
|
||||
/* compute alternate max packet sizes */
|
||||
uif = udev->actconfig->interface[0];
|
||||
|
||||
dev->num_alt = uif->num_altsetting;
|
||||
dev->num_alt = interface->num_altsetting;
|
||||
dev->alt_max_pkt_size = kmalloc(32 * dev->num_alt, GFP_KERNEL);
|
||||
|
||||
if (dev->alt_max_pkt_size == NULL) {
|
||||
|
@ -3113,14 +3148,21 @@ static int em28xx_usb_probe(struct usb_interface *interface,
|
|||
}
|
||||
|
||||
for (i = 0; i < dev->num_alt ; i++) {
|
||||
u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
|
||||
dev->alt_max_pkt_size[i] =
|
||||
(tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
|
||||
u16 tmp = le16_to_cpu(interface->altsetting[i].endpoint[isoc_pipe].desc.wMaxPacketSize);
|
||||
unsigned int size = tmp & 0x7ff;
|
||||
|
||||
if (udev->speed == USB_SPEED_HIGH)
|
||||
size = size * hb_mult(tmp);
|
||||
|
||||
dev->alt_max_pkt_size[i] = size;
|
||||
}
|
||||
|
||||
if ((card[nr] >= 0) && (card[nr] < em28xx_bcount))
|
||||
dev->model = card[nr];
|
||||
|
||||
/* save our data pointer in this interface device */
|
||||
usb_set_intfdata(interface, dev);
|
||||
|
||||
/* allocate device struct */
|
||||
mutex_init(&dev->lock);
|
||||
mutex_lock(&dev->lock);
|
||||
|
@ -3132,9 +3174,6 @@ static int em28xx_usb_probe(struct usb_interface *interface,
|
|||
goto err;
|
||||
}
|
||||
|
||||
/* save our data pointer in this interface device */
|
||||
usb_set_intfdata(interface, dev);
|
||||
|
||||
request_modules(dev);
|
||||
|
||||
/* Should be the last thing to do, to avoid newer udev's to
|
||||
|
@ -3163,6 +3202,13 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
|
|||
if (!dev)
|
||||
return;
|
||||
|
||||
if (dev->is_audio_only) {
|
||||
mutex_lock(&dev->lock);
|
||||
em28xx_close_extension(dev);
|
||||
mutex_unlock(&dev->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
em28xx_info("disconnecting %s\n", dev->vdev->name);
|
||||
|
||||
flush_request_modules(dev);
|
||||
|
|
|
@ -499,17 +499,13 @@ int em28xx_audio_setup(struct em28xx *dev)
|
|||
if (dev->chip_id == CHIP_ID_EM2870 || dev->chip_id == CHIP_ID_EM2874
|
||||
|| dev->chip_id == CHIP_ID_EM28174) {
|
||||
/* Digital only device - don't load any alsa module */
|
||||
dev->audio_mode.has_audio = 0;
|
||||
dev->has_audio_class = 0;
|
||||
dev->has_alsa_audio = 0;
|
||||
dev->audio_mode.has_audio = false;
|
||||
dev->has_audio_class = false;
|
||||
dev->has_alsa_audio = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If device doesn't support Usb Audio Class, use vendor class */
|
||||
if (!dev->has_audio_class)
|
||||
dev->has_alsa_audio = 1;
|
||||
|
||||
dev->audio_mode.has_audio = 1;
|
||||
dev->audio_mode.has_audio = true;
|
||||
|
||||
/* See how this device is configured */
|
||||
cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG);
|
||||
|
@ -519,8 +515,8 @@ int em28xx_audio_setup(struct em28xx *dev)
|
|||
cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */
|
||||
} else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == 0x00) {
|
||||
/* The device doesn't have vendor audio at all */
|
||||
dev->has_alsa_audio = 0;
|
||||
dev->audio_mode.has_audio = 0;
|
||||
dev->has_alsa_audio = false;
|
||||
dev->audio_mode.has_audio = false;
|
||||
return 0;
|
||||
} else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
|
||||
EM28XX_CHIPCFG_I2S_3_SAMPRATES) {
|
||||
|
@ -549,8 +545,8 @@ int em28xx_audio_setup(struct em28xx *dev)
|
|||
*/
|
||||
em28xx_warn("AC97 chip type couldn't be determined\n");
|
||||
dev->audio_mode.ac97 = EM28XX_NO_AC97;
|
||||
dev->has_alsa_audio = 0;
|
||||
dev->audio_mode.has_audio = 0;
|
||||
dev->has_alsa_audio = false;
|
||||
dev->audio_mode.has_audio = false;
|
||||
goto init_audio;
|
||||
}
|
||||
|
||||
|
|
|
@ -487,6 +487,8 @@ struct em28xx {
|
|||
int devno; /* marks the number of this device */
|
||||
enum em28xx_chip_id chip_id;
|
||||
|
||||
int audio_ifnum;
|
||||
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct em28xx_board board;
|
||||
|
||||
|
@ -503,6 +505,7 @@ struct em28xx {
|
|||
|
||||
unsigned int has_audio_class:1;
|
||||
unsigned int has_alsa_audio:1;
|
||||
unsigned int is_audio_only:1;
|
||||
|
||||
/* Controls audio streaming */
|
||||
struct work_struct wq_trigger; /* Trigger to start/stop audio for alsa module */
|
||||
|
|
Loading…
Reference in New Issue