xref: /openbmc/linux/drivers/media/usb/em28xx/em28xx-core.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1f22e9e71SMauro Carvalho Chehab // SPDX-License-Identifier: GPL-2.0+
2f22e9e71SMauro Carvalho Chehab //
3f22e9e71SMauro Carvalho Chehab // em28xx-core.c - driver for Empia EM2800/EM2820/2840 USB video capture devices
4f22e9e71SMauro Carvalho Chehab //
5f22e9e71SMauro Carvalho Chehab // Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it>
6f22e9e71SMauro Carvalho Chehab //		      Markus Rechberger <mrechberger@gmail.com>
732590819SMauro Carvalho Chehab //		      Mauro Carvalho Chehab <mchehab@kernel.org>
8f22e9e71SMauro Carvalho Chehab //		      Sascha Sommer <saschasommer@freenet.de>
9f22e9e71SMauro Carvalho Chehab // Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com>
100c0d06caSMauro Carvalho Chehab 
118314d402SMauro Carvalho Chehab #include "em28xx.h"
128314d402SMauro Carvalho Chehab 
130c0d06caSMauro Carvalho Chehab #include <linux/init.h>
145022a208SMauro Carvalho Chehab #include <linux/jiffies.h>
150c0d06caSMauro Carvalho Chehab #include <linux/list.h>
160c0d06caSMauro Carvalho Chehab #include <linux/module.h>
170c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
180c0d06caSMauro Carvalho Chehab #include <linux/usb.h>
190c0d06caSMauro Carvalho Chehab #include <linux/vmalloc.h>
200c0d06caSMauro Carvalho Chehab #include <sound/ac97_codec.h>
210c0d06caSMauro Carvalho Chehab #include <media/v4l2-common.h>
220c0d06caSMauro Carvalho Chehab 
2301c28193SMauro Carvalho Chehab #define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
2401c28193SMauro Carvalho Chehab 		      "Markus Rechberger <mrechberger@gmail.com>, " \
2532590819SMauro Carvalho Chehab 		      "Mauro Carvalho Chehab <mchehab@kernel.org>, " \
2601c28193SMauro Carvalho Chehab 		      "Sascha Sommer <saschasommer@freenet.de>"
2701c28193SMauro Carvalho Chehab 
2801c28193SMauro Carvalho Chehab MODULE_AUTHOR(DRIVER_AUTHOR);
2901c28193SMauro Carvalho Chehab MODULE_DESCRIPTION(DRIVER_DESC);
30f22e9e71SMauro Carvalho Chehab MODULE_LICENSE("GPL v2");
3101c28193SMauro Carvalho Chehab MODULE_VERSION(EM28XX_VERSION);
3201c28193SMauro Carvalho Chehab 
330c0d06caSMauro Carvalho Chehab /* #define ENABLE_DEBUG_ISOC_FRAMES */
340c0d06caSMauro Carvalho Chehab 
350c0d06caSMauro Carvalho Chehab static unsigned int core_debug;
360c0d06caSMauro Carvalho Chehab module_param(core_debug, int, 0644);
372a96f60eSMauro Carvalho Chehab MODULE_PARM_DESC(core_debug, "enable debug messages [core and isoc]");
380c0d06caSMauro Carvalho Chehab 
390c0d06caSMauro Carvalho Chehab #define em28xx_coredbg(fmt, arg...) do {				\
400c0d06caSMauro Carvalho Chehab 	if (core_debug)							\
4129b05e22SMauro Carvalho Chehab 		dev_printk(KERN_DEBUG, &dev->intf->dev,			\
42ce8591ffSMauro Carvalho Chehab 			   "core: %s: " fmt, __func__, ## arg);		\
43ce8591ffSMauro Carvalho Chehab } while (0)
440c0d06caSMauro Carvalho Chehab 
450c0d06caSMauro Carvalho Chehab static unsigned int reg_debug;
460c0d06caSMauro Carvalho Chehab module_param(reg_debug, int, 0644);
470c0d06caSMauro Carvalho Chehab MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
480c0d06caSMauro Carvalho Chehab 
490c0d06caSMauro Carvalho Chehab #define em28xx_regdbg(fmt, arg...) do {				\
500c0d06caSMauro Carvalho Chehab 	if (reg_debug)							\
5129b05e22SMauro Carvalho Chehab 		dev_printk(KERN_DEBUG, &dev->intf->dev,			\
52ce8591ffSMauro Carvalho Chehab 			   "reg: %s: " fmt, __func__, ## arg);		\
53ce8591ffSMauro Carvalho Chehab } while (0)
540c0d06caSMauro Carvalho Chehab 
55ce8591ffSMauro Carvalho Chehab /* FIXME: don't abuse core_debug */
560c0d06caSMauro Carvalho Chehab #define em28xx_isocdbg(fmt, arg...) do {				\
570c0d06caSMauro Carvalho Chehab 	if (core_debug)							\
5829b05e22SMauro Carvalho Chehab 		dev_printk(KERN_DEBUG, &dev->intf->dev,			\
59ce8591ffSMauro Carvalho Chehab 			   "core: %s: " fmt, __func__, ## arg);		\
60ce8591ffSMauro Carvalho Chehab } while (0)
610c0d06caSMauro Carvalho Chehab 
620c0d06caSMauro Carvalho Chehab /*
630c0d06caSMauro Carvalho Chehab  * em28xx_read_reg_req()
640c0d06caSMauro Carvalho Chehab  * reads data from the usb device specifying bRequest
650c0d06caSMauro Carvalho Chehab  */
em28xx_read_reg_req_len(struct em28xx * dev,u8 req,u16 reg,char * buf,int len)660c0d06caSMauro Carvalho Chehab int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
670c0d06caSMauro Carvalho Chehab 			    char *buf, int len)
680c0d06caSMauro Carvalho Chehab {
690c0d06caSMauro Carvalho Chehab 	int ret;
70c6d48134SMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(dev->intf);
71c6d48134SMauro Carvalho Chehab 	int pipe = usb_rcvctrlpipe(udev, 0);
720c0d06caSMauro Carvalho Chehab 
732665c299SFrank Schaefer 	if (dev->disconnected)
740c0d06caSMauro Carvalho Chehab 		return -ENODEV;
750c0d06caSMauro Carvalho Chehab 
760c0d06caSMauro Carvalho Chehab 	if (len > URB_MAX_CTRL_SIZE)
770c0d06caSMauro Carvalho Chehab 		return -EINVAL;
780c0d06caSMauro Carvalho Chehab 
790c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->ctrl_urb_lock);
80c6d48134SMauro Carvalho Chehab 	ret = usb_control_msg(udev, pipe, req,
810c0d06caSMauro Carvalho Chehab 			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
82*d9b7e8dfSJohan Hovold 			      0x0000, reg, dev->urb_buf, len, 1000);
830c0d06caSMauro Carvalho Chehab 	if (ret < 0) {
84603c33a3SFrank Schaefer 		em28xx_regdbg("(pipe 0x%08x): IN:  %02x %02x %02x %02x %02x %02x %02x %02x  failed with error %i\n",
85349ac5bbSMauro Carvalho Chehab 			      pipe,
86349ac5bbSMauro Carvalho Chehab 			      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
87ce8591ffSMauro Carvalho Chehab 			      req, 0, 0,
88ce8591ffSMauro Carvalho Chehab 			      reg & 0xff, reg >> 8,
89603c33a3SFrank Schaefer 			      len & 0xff, len >> 8, ret);
900c0d06caSMauro Carvalho Chehab 		mutex_unlock(&dev->ctrl_urb_lock);
9145f04e82SFrank Schaefer 		return usb_translate_errors(ret);
920c0d06caSMauro Carvalho Chehab 	}
930c0d06caSMauro Carvalho Chehab 
940c0d06caSMauro Carvalho Chehab 	if (len)
950c0d06caSMauro Carvalho Chehab 		memcpy(buf, dev->urb_buf, len);
960c0d06caSMauro Carvalho Chehab 
970c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->ctrl_urb_lock);
980c0d06caSMauro Carvalho Chehab 
99603c33a3SFrank Schaefer 	em28xx_regdbg("(pipe 0x%08x): IN:  %02x %02x %02x %02x %02x %02x %02x %02x <<< %*ph\n",
100ce8591ffSMauro Carvalho Chehab 		      pipe, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
101ce8591ffSMauro Carvalho Chehab 		      req, 0, 0,
102ce8591ffSMauro Carvalho Chehab 		      reg & 0xff, reg >> 8,
103ce8591ffSMauro Carvalho Chehab 		      len & 0xff, len >> 8, len, buf);
1040c0d06caSMauro Carvalho Chehab 
1050c0d06caSMauro Carvalho Chehab 	return ret;
1060c0d06caSMauro Carvalho Chehab }
1070c0d06caSMauro Carvalho Chehab 
1080c0d06caSMauro Carvalho Chehab /*
1090c0d06caSMauro Carvalho Chehab  * em28xx_read_reg_req()
1100c0d06caSMauro Carvalho Chehab  * reads data from the usb device specifying bRequest
1110c0d06caSMauro Carvalho Chehab  */
em28xx_read_reg_req(struct em28xx * dev,u8 req,u16 reg)1120c0d06caSMauro Carvalho Chehab int em28xx_read_reg_req(struct em28xx *dev, u8 req, u16 reg)
1130c0d06caSMauro Carvalho Chehab {
1140c0d06caSMauro Carvalho Chehab 	int ret;
1150c0d06caSMauro Carvalho Chehab 	u8 val;
1160c0d06caSMauro Carvalho Chehab 
1170c0d06caSMauro Carvalho Chehab 	ret = em28xx_read_reg_req_len(dev, req, reg, &val, 1);
1180c0d06caSMauro Carvalho Chehab 	if (ret < 0)
1190c0d06caSMauro Carvalho Chehab 		return ret;
1200c0d06caSMauro Carvalho Chehab 
1210c0d06caSMauro Carvalho Chehab 	return val;
1220c0d06caSMauro Carvalho Chehab }
1230c0d06caSMauro Carvalho Chehab 
em28xx_read_reg(struct em28xx * dev,u16 reg)1240c0d06caSMauro Carvalho Chehab int em28xx_read_reg(struct em28xx *dev, u16 reg)
1250c0d06caSMauro Carvalho Chehab {
1260c0d06caSMauro Carvalho Chehab 	return em28xx_read_reg_req(dev, USB_REQ_GET_STATUS, reg);
1270c0d06caSMauro Carvalho Chehab }
1280c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(em28xx_read_reg);
1290c0d06caSMauro Carvalho Chehab 
1300c0d06caSMauro Carvalho Chehab /*
1310c0d06caSMauro Carvalho Chehab  * em28xx_write_regs_req()
1320c0d06caSMauro Carvalho Chehab  * sends data to the usb device, specifying bRequest
1330c0d06caSMauro Carvalho Chehab  */
em28xx_write_regs_req(struct em28xx * dev,u8 req,u16 reg,char * buf,int len)1340c0d06caSMauro Carvalho Chehab int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
1350c0d06caSMauro Carvalho Chehab 			  int len)
1360c0d06caSMauro Carvalho Chehab {
1370c0d06caSMauro Carvalho Chehab 	int ret;
138c6d48134SMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(dev->intf);
139c6d48134SMauro Carvalho Chehab 	int pipe = usb_sndctrlpipe(udev, 0);
1400c0d06caSMauro Carvalho Chehab 
1412665c299SFrank Schaefer 	if (dev->disconnected)
1420c0d06caSMauro Carvalho Chehab 		return -ENODEV;
1430c0d06caSMauro Carvalho Chehab 
144349ac5bbSMauro Carvalho Chehab 	if (len < 1 || len > URB_MAX_CTRL_SIZE)
1450c0d06caSMauro Carvalho Chehab 		return -EINVAL;
1460c0d06caSMauro Carvalho Chehab 
1470c0d06caSMauro Carvalho Chehab 	mutex_lock(&dev->ctrl_urb_lock);
1480c0d06caSMauro Carvalho Chehab 	memcpy(dev->urb_buf, buf, len);
149c6d48134SMauro Carvalho Chehab 	ret = usb_control_msg(udev, pipe, req,
1500c0d06caSMauro Carvalho Chehab 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
151*d9b7e8dfSJohan Hovold 			      0x0000, reg, dev->urb_buf, len, 1000);
1520c0d06caSMauro Carvalho Chehab 	mutex_unlock(&dev->ctrl_urb_lock);
1530c0d06caSMauro Carvalho Chehab 
154603c33a3SFrank Schaefer 	if (ret < 0) {
155603c33a3SFrank Schaefer 		em28xx_regdbg("(pipe 0x%08x): OUT:  %02x %02x %02x %02x %02x %02x %02x %02x >>> %*ph  failed with error %i\n",
156603c33a3SFrank Schaefer 			      pipe,
157603c33a3SFrank Schaefer 			      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
158603c33a3SFrank Schaefer 			      req, 0, 0,
159603c33a3SFrank Schaefer 			      reg & 0xff, reg >> 8,
160603c33a3SFrank Schaefer 			      len & 0xff, len >> 8, len, buf, ret);
16145f04e82SFrank Schaefer 		return usb_translate_errors(ret);
162603c33a3SFrank Schaefer 	}
163603c33a3SFrank Schaefer 
164603c33a3SFrank Schaefer 	em28xx_regdbg("(pipe 0x%08x): OUT:  %02x %02x %02x %02x %02x %02x %02x %02x >>> %*ph\n",
165603c33a3SFrank Schaefer 		      pipe,
166603c33a3SFrank Schaefer 		      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
167603c33a3SFrank Schaefer 		      req, 0, 0,
168603c33a3SFrank Schaefer 		      reg & 0xff, reg >> 8,
169603c33a3SFrank Schaefer 		      len & 0xff, len >> 8, len, buf);
17045f04e82SFrank Schaefer 
1710c0d06caSMauro Carvalho Chehab 	if (dev->wait_after_write)
1720c0d06caSMauro Carvalho Chehab 		msleep(dev->wait_after_write);
1730c0d06caSMauro Carvalho Chehab 
1740c0d06caSMauro Carvalho Chehab 	return ret;
1750c0d06caSMauro Carvalho Chehab }
1760c0d06caSMauro Carvalho Chehab 
em28xx_write_regs(struct em28xx * dev,u16 reg,char * buf,int len)1770c0d06caSMauro Carvalho Chehab int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
1780c0d06caSMauro Carvalho Chehab {
1796914d70eSFrank Schaefer 	return em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len);
1800c0d06caSMauro Carvalho Chehab }
1810c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(em28xx_write_regs);
1820c0d06caSMauro Carvalho Chehab 
1830c0d06caSMauro Carvalho Chehab /* Write a single register */
em28xx_write_reg(struct em28xx * dev,u16 reg,u8 val)1840c0d06caSMauro Carvalho Chehab int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val)
1850c0d06caSMauro Carvalho Chehab {
1860c0d06caSMauro Carvalho Chehab 	return em28xx_write_regs(dev, reg, &val, 1);
1870c0d06caSMauro Carvalho Chehab }
1880c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(em28xx_write_reg);
1890c0d06caSMauro Carvalho Chehab 
1900c0d06caSMauro Carvalho Chehab /*
1910c0d06caSMauro Carvalho Chehab  * em28xx_write_reg_bits()
1920c0d06caSMauro Carvalho Chehab  * sets only some bits (specified by bitmask) of a register, by first reading
1930c0d06caSMauro Carvalho Chehab  * the actual value
1940c0d06caSMauro Carvalho Chehab  */
em28xx_write_reg_bits(struct em28xx * dev,u16 reg,u8 val,u8 bitmask)1950c0d06caSMauro Carvalho Chehab int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
1960c0d06caSMauro Carvalho Chehab 			  u8 bitmask)
1970c0d06caSMauro Carvalho Chehab {
1980c0d06caSMauro Carvalho Chehab 	int oldval;
1990c0d06caSMauro Carvalho Chehab 	u8 newval;
2000c0d06caSMauro Carvalho Chehab 
2010c0d06caSMauro Carvalho Chehab 	oldval = em28xx_read_reg(dev, reg);
2020c0d06caSMauro Carvalho Chehab 	if (oldval < 0)
2030c0d06caSMauro Carvalho Chehab 		return oldval;
2040c0d06caSMauro Carvalho Chehab 
2050c0d06caSMauro Carvalho Chehab 	newval = (((u8)oldval) & ~bitmask) | (val & bitmask);
2060c0d06caSMauro Carvalho Chehab 
2070c0d06caSMauro Carvalho Chehab 	return em28xx_write_regs(dev, reg, &newval, 1);
2080c0d06caSMauro Carvalho Chehab }
2090c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(em28xx_write_reg_bits);
2100c0d06caSMauro Carvalho Chehab 
2110c0d06caSMauro Carvalho Chehab /*
2126063d077SFrank Schaefer  * em28xx_toggle_reg_bits()
2136063d077SFrank Schaefer  * toggles/inverts the bits (specified by bitmask) of a register
2146063d077SFrank Schaefer  */
em28xx_toggle_reg_bits(struct em28xx * dev,u16 reg,u8 bitmask)2156063d077SFrank Schaefer int em28xx_toggle_reg_bits(struct em28xx *dev, u16 reg, u8 bitmask)
2166063d077SFrank Schaefer {
2176063d077SFrank Schaefer 	int oldval;
2186063d077SFrank Schaefer 	u8 newval;
2196063d077SFrank Schaefer 
2206063d077SFrank Schaefer 	oldval = em28xx_read_reg(dev, reg);
2216063d077SFrank Schaefer 	if (oldval < 0)
2226063d077SFrank Schaefer 		return oldval;
2236063d077SFrank Schaefer 
2246063d077SFrank Schaefer 	newval = (~oldval & bitmask) | (oldval & ~bitmask);
2256063d077SFrank Schaefer 
2266063d077SFrank Schaefer 	return em28xx_write_reg(dev, reg, newval);
2276063d077SFrank Schaefer }
2286063d077SFrank Schaefer EXPORT_SYMBOL_GPL(em28xx_toggle_reg_bits);
2296063d077SFrank Schaefer 
2306063d077SFrank Schaefer /*
2310c0d06caSMauro Carvalho Chehab  * em28xx_is_ac97_ready()
2320c0d06caSMauro Carvalho Chehab  * Checks if ac97 is ready
2330c0d06caSMauro Carvalho Chehab  */
em28xx_is_ac97_ready(struct em28xx * dev)2340c0d06caSMauro Carvalho Chehab static int em28xx_is_ac97_ready(struct em28xx *dev)
2350c0d06caSMauro Carvalho Chehab {
2365022a208SMauro Carvalho Chehab 	unsigned long timeout = jiffies + msecs_to_jiffies(EM28XX_AC97_XFER_TIMEOUT);
2375022a208SMauro Carvalho Chehab 	int ret;
2380c0d06caSMauro Carvalho Chehab 
2390c0d06caSMauro Carvalho Chehab 	/* Wait up to 50 ms for AC97 command to complete */
2405022a208SMauro Carvalho Chehab 	while (time_is_after_jiffies(timeout)) {
2410c0d06caSMauro Carvalho Chehab 		ret = em28xx_read_reg(dev, EM28XX_R43_AC97BUSY);
2420c0d06caSMauro Carvalho Chehab 		if (ret < 0)
2430c0d06caSMauro Carvalho Chehab 			return ret;
2440c0d06caSMauro Carvalho Chehab 
2450c0d06caSMauro Carvalho Chehab 		if (!(ret & 0x01))
2460c0d06caSMauro Carvalho Chehab 			return 0;
2475022a208SMauro Carvalho Chehab 		msleep(5);
2480c0d06caSMauro Carvalho Chehab 	}
2490c0d06caSMauro Carvalho Chehab 
25029b05e22SMauro Carvalho Chehab 	dev_warn(&dev->intf->dev,
251ce8591ffSMauro Carvalho Chehab 		 "AC97 command still being executed: not handled properly!\n");
2520c0d06caSMauro Carvalho Chehab 	return -EBUSY;
2530c0d06caSMauro Carvalho Chehab }
2540c0d06caSMauro Carvalho Chehab 
2550c0d06caSMauro Carvalho Chehab /*
2560c0d06caSMauro Carvalho Chehab  * em28xx_read_ac97()
2570c0d06caSMauro Carvalho Chehab  * write a 16 bit value to the specified AC97 address (LSB first!)
2580c0d06caSMauro Carvalho Chehab  */
em28xx_read_ac97(struct em28xx * dev,u8 reg)2590c0d06caSMauro Carvalho Chehab int em28xx_read_ac97(struct em28xx *dev, u8 reg)
2600c0d06caSMauro Carvalho Chehab {
2610c0d06caSMauro Carvalho Chehab 	int ret;
2620c0d06caSMauro Carvalho Chehab 	u8 addr = (reg & 0x7f) | 0x80;
2634a9e512aSHans Verkuil 	__le16 val;
2640c0d06caSMauro Carvalho Chehab 
2650c0d06caSMauro Carvalho Chehab 	ret = em28xx_is_ac97_ready(dev);
2660c0d06caSMauro Carvalho Chehab 	if (ret < 0)
2670c0d06caSMauro Carvalho Chehab 		return ret;
2680c0d06caSMauro Carvalho Chehab 
2690c0d06caSMauro Carvalho Chehab 	ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1);
2700c0d06caSMauro Carvalho Chehab 	if (ret < 0)
2710c0d06caSMauro Carvalho Chehab 		return ret;
2720c0d06caSMauro Carvalho Chehab 
2730c0d06caSMauro Carvalho Chehab 	ret = dev->em28xx_read_reg_req_len(dev, 0, EM28XX_R40_AC97LSB,
2740c0d06caSMauro Carvalho Chehab 					   (u8 *)&val, sizeof(val));
2750c0d06caSMauro Carvalho Chehab 
2760c0d06caSMauro Carvalho Chehab 	if (ret < 0)
2770c0d06caSMauro Carvalho Chehab 		return ret;
2780c0d06caSMauro Carvalho Chehab 	return le16_to_cpu(val);
2790c0d06caSMauro Carvalho Chehab }
2800c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(em28xx_read_ac97);
2810c0d06caSMauro Carvalho Chehab 
2820c0d06caSMauro Carvalho Chehab /*
2830c0d06caSMauro Carvalho Chehab  * em28xx_write_ac97()
2840c0d06caSMauro Carvalho Chehab  * write a 16 bit value to the specified AC97 address (LSB first!)
2850c0d06caSMauro Carvalho Chehab  */
em28xx_write_ac97(struct em28xx * dev,u8 reg,u16 val)2860c0d06caSMauro Carvalho Chehab int em28xx_write_ac97(struct em28xx *dev, u8 reg, u16 val)
2870c0d06caSMauro Carvalho Chehab {
2880c0d06caSMauro Carvalho Chehab 	int ret;
2890c0d06caSMauro Carvalho Chehab 	u8 addr = reg & 0x7f;
2900c0d06caSMauro Carvalho Chehab 	__le16 value;
2910c0d06caSMauro Carvalho Chehab 
2920c0d06caSMauro Carvalho Chehab 	value = cpu_to_le16(val);
2930c0d06caSMauro Carvalho Chehab 
2940c0d06caSMauro Carvalho Chehab 	ret = em28xx_is_ac97_ready(dev);
2950c0d06caSMauro Carvalho Chehab 	if (ret < 0)
2960c0d06caSMauro Carvalho Chehab 		return ret;
2970c0d06caSMauro Carvalho Chehab 
2980c0d06caSMauro Carvalho Chehab 	ret = em28xx_write_regs(dev, EM28XX_R40_AC97LSB, (u8 *)&value, 2);
2990c0d06caSMauro Carvalho Chehab 	if (ret < 0)
3000c0d06caSMauro Carvalho Chehab 		return ret;
3010c0d06caSMauro Carvalho Chehab 
3020c0d06caSMauro Carvalho Chehab 	ret = em28xx_write_regs(dev, EM28XX_R42_AC97ADDR, &addr, 1);
3030c0d06caSMauro Carvalho Chehab 	if (ret < 0)
3040c0d06caSMauro Carvalho Chehab 		return ret;
3050c0d06caSMauro Carvalho Chehab 
3060c0d06caSMauro Carvalho Chehab 	return 0;
3070c0d06caSMauro Carvalho Chehab }
3080c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(em28xx_write_ac97);
3090c0d06caSMauro Carvalho Chehab 
3100c0d06caSMauro Carvalho Chehab struct em28xx_vol_itable {
3110c0d06caSMauro Carvalho Chehab 	enum em28xx_amux mux;
3120c0d06caSMauro Carvalho Chehab 	u8		 reg;
3130c0d06caSMauro Carvalho Chehab };
3140c0d06caSMauro Carvalho Chehab 
3150c0d06caSMauro Carvalho Chehab static struct em28xx_vol_itable inputs[] = {
3160c0d06caSMauro Carvalho Chehab 	{ EM28XX_AMUX_VIDEO,	AC97_VIDEO	},
3170c0d06caSMauro Carvalho Chehab 	{ EM28XX_AMUX_LINE_IN,	AC97_LINE	},
3180c0d06caSMauro Carvalho Chehab 	{ EM28XX_AMUX_PHONE,	AC97_PHONE	},
3190c0d06caSMauro Carvalho Chehab 	{ EM28XX_AMUX_MIC,	AC97_MIC	},
3200c0d06caSMauro Carvalho Chehab 	{ EM28XX_AMUX_CD,	AC97_CD		},
3210c0d06caSMauro Carvalho Chehab 	{ EM28XX_AMUX_AUX,	AC97_AUX	},
3220c0d06caSMauro Carvalho Chehab 	{ EM28XX_AMUX_PCM_OUT,	AC97_PCM	},
3230c0d06caSMauro Carvalho Chehab };
3240c0d06caSMauro Carvalho Chehab 
set_ac97_input(struct em28xx * dev)3250c0d06caSMauro Carvalho Chehab static int set_ac97_input(struct em28xx *dev)
3260c0d06caSMauro Carvalho Chehab {
3270c0d06caSMauro Carvalho Chehab 	int ret, i;
3280c0d06caSMauro Carvalho Chehab 	enum em28xx_amux amux = dev->ctl_ainput;
3290c0d06caSMauro Carvalho Chehab 
330349ac5bbSMauro Carvalho Chehab 	/*
331349ac5bbSMauro Carvalho Chehab 	 * EM28XX_AMUX_VIDEO2 is a special case used to indicate that
332349ac5bbSMauro Carvalho Chehab 	 * em28xx should point to LINE IN, while AC97 should use VIDEO
3330c0d06caSMauro Carvalho Chehab 	 */
3340c0d06caSMauro Carvalho Chehab 	if (amux == EM28XX_AMUX_VIDEO2)
3350c0d06caSMauro Carvalho Chehab 		amux = EM28XX_AMUX_VIDEO;
3360c0d06caSMauro Carvalho Chehab 
3370c0d06caSMauro Carvalho Chehab 	/* Mute all entres but the one that were selected */
3380c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(inputs); i++) {
3390c0d06caSMauro Carvalho Chehab 		if (amux == inputs[i].mux)
3400c0d06caSMauro Carvalho Chehab 			ret = em28xx_write_ac97(dev, inputs[i].reg, 0x0808);
3410c0d06caSMauro Carvalho Chehab 		else
3420c0d06caSMauro Carvalho Chehab 			ret = em28xx_write_ac97(dev, inputs[i].reg, 0x8000);
3430c0d06caSMauro Carvalho Chehab 
3440c0d06caSMauro Carvalho Chehab 		if (ret < 0)
34529b05e22SMauro Carvalho Chehab 			dev_warn(&dev->intf->dev,
346ce8591ffSMauro Carvalho Chehab 				 "couldn't setup AC97 register %d\n",
3470c0d06caSMauro Carvalho Chehab 				 inputs[i].reg);
3480c0d06caSMauro Carvalho Chehab 	}
3490c0d06caSMauro Carvalho Chehab 	return 0;
3500c0d06caSMauro Carvalho Chehab }
3510c0d06caSMauro Carvalho Chehab 
em28xx_set_audio_source(struct em28xx * dev)3520c0d06caSMauro Carvalho Chehab static int em28xx_set_audio_source(struct em28xx *dev)
3530c0d06caSMauro Carvalho Chehab {
3540c0d06caSMauro Carvalho Chehab 	int ret;
3550c0d06caSMauro Carvalho Chehab 	u8 input;
3560c0d06caSMauro Carvalho Chehab 
3570c0d06caSMauro Carvalho Chehab 	if (dev->board.is_em2800) {
3580c0d06caSMauro Carvalho Chehab 		if (dev->ctl_ainput == EM28XX_AMUX_VIDEO)
3590c0d06caSMauro Carvalho Chehab 			input = EM2800_AUDIO_SRC_TUNER;
3600c0d06caSMauro Carvalho Chehab 		else
3610c0d06caSMauro Carvalho Chehab 			input = EM2800_AUDIO_SRC_LINE;
3620c0d06caSMauro Carvalho Chehab 
3630c0d06caSMauro Carvalho Chehab 		ret = em28xx_write_regs(dev, EM2800_R08_AUDIOSRC, &input, 1);
3640c0d06caSMauro Carvalho Chehab 		if (ret < 0)
3650c0d06caSMauro Carvalho Chehab 			return ret;
3660c0d06caSMauro Carvalho Chehab 	}
3670c0d06caSMauro Carvalho Chehab 
368349ac5bbSMauro Carvalho Chehab 	if (dev->has_msp34xx) {
3690c0d06caSMauro Carvalho Chehab 		input = EM28XX_AUDIO_SRC_TUNER;
370349ac5bbSMauro Carvalho Chehab 	} else {
3710c0d06caSMauro Carvalho Chehab 		switch (dev->ctl_ainput) {
3720c0d06caSMauro Carvalho Chehab 		case EM28XX_AMUX_VIDEO:
3730c0d06caSMauro Carvalho Chehab 			input = EM28XX_AUDIO_SRC_TUNER;
3740c0d06caSMauro Carvalho Chehab 			break;
3750c0d06caSMauro Carvalho Chehab 		default:
3760c0d06caSMauro Carvalho Chehab 			input = EM28XX_AUDIO_SRC_LINE;
3770c0d06caSMauro Carvalho Chehab 			break;
3780c0d06caSMauro Carvalho Chehab 		}
3790c0d06caSMauro Carvalho Chehab 	}
3800c0d06caSMauro Carvalho Chehab 
3810c0d06caSMauro Carvalho Chehab 	if (dev->board.mute_gpio && dev->mute)
3820c0d06caSMauro Carvalho Chehab 		em28xx_gpio_set(dev, dev->board.mute_gpio);
3830c0d06caSMauro Carvalho Chehab 	else
3840c0d06caSMauro Carvalho Chehab 		em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio);
3850c0d06caSMauro Carvalho Chehab 
3860c0d06caSMauro Carvalho Chehab 	ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0);
3870c0d06caSMauro Carvalho Chehab 	if (ret < 0)
3880c0d06caSMauro Carvalho Chehab 		return ret;
389349ac5bbSMauro Carvalho Chehab 	usleep_range(10000, 11000);
3900c0d06caSMauro Carvalho Chehab 
3910c0d06caSMauro Carvalho Chehab 	switch (dev->audio_mode.ac97) {
3920c0d06caSMauro Carvalho Chehab 	case EM28XX_NO_AC97:
3930c0d06caSMauro Carvalho Chehab 		break;
3940c0d06caSMauro Carvalho Chehab 	default:
3950c0d06caSMauro Carvalho Chehab 		ret = set_ac97_input(dev);
3960c0d06caSMauro Carvalho Chehab 	}
3970c0d06caSMauro Carvalho Chehab 
3980c0d06caSMauro Carvalho Chehab 	return ret;
3990c0d06caSMauro Carvalho Chehab }
4000c0d06caSMauro Carvalho Chehab 
4010c0d06caSMauro Carvalho Chehab struct em28xx_vol_otable {
4020c0d06caSMauro Carvalho Chehab 	enum em28xx_aout mux;
4030c0d06caSMauro Carvalho Chehab 	u8		 reg;
4040c0d06caSMauro Carvalho Chehab };
4050c0d06caSMauro Carvalho Chehab 
4060c0d06caSMauro Carvalho Chehab static const struct em28xx_vol_otable outputs[] = {
4070c0d06caSMauro Carvalho Chehab 	{ EM28XX_AOUT_MASTER, AC97_MASTER		},
4080c0d06caSMauro Carvalho Chehab 	{ EM28XX_AOUT_LINE,   AC97_HEADPHONE		},
4090c0d06caSMauro Carvalho Chehab 	{ EM28XX_AOUT_MONO,   AC97_MASTER_MONO		},
4100c0d06caSMauro Carvalho Chehab 	{ EM28XX_AOUT_LFE,    AC97_CENTER_LFE_MASTER	},
4110c0d06caSMauro Carvalho Chehab 	{ EM28XX_AOUT_SURR,   AC97_SURROUND_MASTER	},
4120c0d06caSMauro Carvalho Chehab };
4130c0d06caSMauro Carvalho Chehab 
em28xx_audio_analog_set(struct em28xx * dev)4140c0d06caSMauro Carvalho Chehab int em28xx_audio_analog_set(struct em28xx *dev)
4150c0d06caSMauro Carvalho Chehab {
4160c0d06caSMauro Carvalho Chehab 	int ret, i;
4170c0d06caSMauro Carvalho Chehab 	u8 xclk;
4180c0d06caSMauro Carvalho Chehab 
419920f1e4aSFrank Schaefer 	if (dev->int_audio_type == EM28XX_INT_AUDIO_NONE)
4200c0d06caSMauro Carvalho Chehab 		return 0;
4210c0d06caSMauro Carvalho Chehab 
422349ac5bbSMauro Carvalho Chehab 	/*
423349ac5bbSMauro Carvalho Chehab 	 * It is assumed that all devices use master volume for output.
424349ac5bbSMauro Carvalho Chehab 	 * It would be possible to use also line output.
4250c0d06caSMauro Carvalho Chehab 	 */
4260c0d06caSMauro Carvalho Chehab 	if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
4270c0d06caSMauro Carvalho Chehab 		/* Mute all outputs */
4280c0d06caSMauro Carvalho Chehab 		for (i = 0; i < ARRAY_SIZE(outputs); i++) {
4290c0d06caSMauro Carvalho Chehab 			ret = em28xx_write_ac97(dev, outputs[i].reg, 0x8000);
4300c0d06caSMauro Carvalho Chehab 			if (ret < 0)
43129b05e22SMauro Carvalho Chehab 				dev_warn(&dev->intf->dev,
432ce8591ffSMauro Carvalho Chehab 					 "couldn't setup AC97 register %d\n",
4330c0d06caSMauro Carvalho Chehab 					 outputs[i].reg);
4340c0d06caSMauro Carvalho Chehab 		}
4350c0d06caSMauro Carvalho Chehab 	}
4360c0d06caSMauro Carvalho Chehab 
4370c0d06caSMauro Carvalho Chehab 	xclk = dev->board.xclk & 0x7f;
4380c0d06caSMauro Carvalho Chehab 	if (!dev->mute)
4390c0d06caSMauro Carvalho Chehab 		xclk |= EM28XX_XCLK_AUDIO_UNMUTE;
4400c0d06caSMauro Carvalho Chehab 
4410c0d06caSMauro Carvalho Chehab 	ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk);
4420c0d06caSMauro Carvalho Chehab 	if (ret < 0)
4430c0d06caSMauro Carvalho Chehab 		return ret;
444349ac5bbSMauro Carvalho Chehab 	usleep_range(10000, 11000);
4450c0d06caSMauro Carvalho Chehab 
4460c0d06caSMauro Carvalho Chehab 	/* Selects the proper audio input */
4470c0d06caSMauro Carvalho Chehab 	ret = em28xx_set_audio_source(dev);
4480c0d06caSMauro Carvalho Chehab 
4490c0d06caSMauro Carvalho Chehab 	/* Sets volume */
4500c0d06caSMauro Carvalho Chehab 	if (dev->audio_mode.ac97 != EM28XX_NO_AC97) {
4510c0d06caSMauro Carvalho Chehab 		int vol;
4520c0d06caSMauro Carvalho Chehab 
4530c0d06caSMauro Carvalho Chehab 		em28xx_write_ac97(dev, AC97_POWERDOWN, 0x4200);
4540c0d06caSMauro Carvalho Chehab 		em28xx_write_ac97(dev, AC97_EXTENDED_STATUS, 0x0031);
4550c0d06caSMauro Carvalho Chehab 		em28xx_write_ac97(dev, AC97_PCM_LR_ADC_RATE, 0xbb80);
4560c0d06caSMauro Carvalho Chehab 
4570c0d06caSMauro Carvalho Chehab 		/* LSB: left channel - both channels with the same level */
4580c0d06caSMauro Carvalho Chehab 		vol = (0x1f - dev->volume) | ((0x1f - dev->volume) << 8);
4590c0d06caSMauro Carvalho Chehab 
4600c0d06caSMauro Carvalho Chehab 		/* Mute device, if needed */
4610c0d06caSMauro Carvalho Chehab 		if (dev->mute)
4620c0d06caSMauro Carvalho Chehab 			vol |= 0x8000;
4630c0d06caSMauro Carvalho Chehab 
4640c0d06caSMauro Carvalho Chehab 		/* Sets volume */
4650c0d06caSMauro Carvalho Chehab 		for (i = 0; i < ARRAY_SIZE(outputs); i++) {
4660c0d06caSMauro Carvalho Chehab 			if (dev->ctl_aoutput & outputs[i].mux)
4670c0d06caSMauro Carvalho Chehab 				ret = em28xx_write_ac97(dev, outputs[i].reg,
4680c0d06caSMauro Carvalho Chehab 							vol);
4690c0d06caSMauro Carvalho Chehab 			if (ret < 0)
47029b05e22SMauro Carvalho Chehab 				dev_warn(&dev->intf->dev,
471ce8591ffSMauro Carvalho Chehab 					 "couldn't setup AC97 register %d\n",
4720c0d06caSMauro Carvalho Chehab 					 outputs[i].reg);
4730c0d06caSMauro Carvalho Chehab 		}
4740c0d06caSMauro Carvalho Chehab 
4750c0d06caSMauro Carvalho Chehab 		if (dev->ctl_aoutput & EM28XX_AOUT_PCM_IN) {
4760c0d06caSMauro Carvalho Chehab 			int sel = ac97_return_record_select(dev->ctl_aoutput);
4770c0d06caSMauro Carvalho Chehab 
478349ac5bbSMauro Carvalho Chehab 			/*
479349ac5bbSMauro Carvalho Chehab 			 * Use the same input for both left and right
480349ac5bbSMauro Carvalho Chehab 			 * channels
481349ac5bbSMauro Carvalho Chehab 			 */
4820c0d06caSMauro Carvalho Chehab 			sel |= (sel << 8);
4830c0d06caSMauro Carvalho Chehab 
4840c0d06caSMauro Carvalho Chehab 			em28xx_write_ac97(dev, AC97_REC_SEL, sel);
4850c0d06caSMauro Carvalho Chehab 		}
4860c0d06caSMauro Carvalho Chehab 	}
4870c0d06caSMauro Carvalho Chehab 
4880c0d06caSMauro Carvalho Chehab 	return ret;
4890c0d06caSMauro Carvalho Chehab }
4900c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(em28xx_audio_analog_set);
4910c0d06caSMauro Carvalho Chehab 
em28xx_audio_setup(struct em28xx * dev)4920c0d06caSMauro Carvalho Chehab int em28xx_audio_setup(struct em28xx *dev)
4930c0d06caSMauro Carvalho Chehab {
4940c0d06caSMauro Carvalho Chehab 	int vid1, vid2, feat, cfg;
495430e3572SHans Verkuil 	u32 vid = 0;
49660a24ba0SFrank Schaefer 	u8 i2s_samplerates;
4970c0d06caSMauro Carvalho Chehab 
498fb91bde9SFrank Schaefer 	if (dev->chip_id == CHIP_ID_EM2870 ||
499fb91bde9SFrank Schaefer 	    dev->chip_id == CHIP_ID_EM2874 ||
500fb91bde9SFrank Schaefer 	    dev->chip_id == CHIP_ID_EM28174 ||
501fb91bde9SFrank Schaefer 	    dev->chip_id == CHIP_ID_EM28178) {
502fb91bde9SFrank Schaefer 		/* Digital only device - don't load any alsa module */
503920f1e4aSFrank Schaefer 		dev->int_audio_type = EM28XX_INT_AUDIO_NONE;
504c5874208SFrank Schaefer 		dev->usb_audio_type = EM28XX_USB_AUDIO_NONE;
5050c0d06caSMauro Carvalho Chehab 		return 0;
506fb91bde9SFrank Schaefer 	}
507fb91bde9SFrank Schaefer 
5080c0d06caSMauro Carvalho Chehab 	/* See how this device is configured */
5090c0d06caSMauro Carvalho Chehab 	cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG);
51029b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Config register raw data: 0x%02x\n", cfg);
51143c3ea31SFrank Schaefer 	if (cfg < 0) { /* Register read error */
51243c3ea31SFrank Schaefer 		/* Be conservative */
513920f1e4aSFrank Schaefer 		dev->int_audio_type = EM28XX_INT_AUDIO_AC97;
5140c0d06caSMauro Carvalho Chehab 	} else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == 0x00) {
5150c0d06caSMauro Carvalho Chehab 		/* The device doesn't have vendor audio at all */
516920f1e4aSFrank Schaefer 		dev->int_audio_type = EM28XX_INT_AUDIO_NONE;
517c5874208SFrank Schaefer 		dev->usb_audio_type = EM28XX_USB_AUDIO_NONE;
5180c0d06caSMauro Carvalho Chehab 		return 0;
519687ff8b0SFrank Schaefer 	} else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) != EM28XX_CHIPCFG_AC97) {
520920f1e4aSFrank Schaefer 		dev->int_audio_type = EM28XX_INT_AUDIO_I2S;
521687ff8b0SFrank Schaefer 		if (dev->chip_id < CHIP_ID_EM2860 &&
522687ff8b0SFrank Schaefer 		    (cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
523687ff8b0SFrank Schaefer 		    EM2820_CHIPCFG_I2S_1_SAMPRATE)
52460a24ba0SFrank Schaefer 			i2s_samplerates = 1;
525687ff8b0SFrank Schaefer 		else if (dev->chip_id >= CHIP_ID_EM2860 &&
526687ff8b0SFrank Schaefer 			 (cfg & EM28XX_CHIPCFG_AUDIOMASK) ==
527687ff8b0SFrank Schaefer 			 EM2860_CHIPCFG_I2S_5_SAMPRATES)
52860a24ba0SFrank Schaefer 			i2s_samplerates = 5;
529687ff8b0SFrank Schaefer 		else
53060a24ba0SFrank Schaefer 			i2s_samplerates = 3;
53129b05e22SMauro Carvalho Chehab 		dev_info(&dev->intf->dev, "I2S Audio (%d sample rate(s))\n",
53260a24ba0SFrank Schaefer 			 i2s_samplerates);
5330c0d06caSMauro Carvalho Chehab 		/* Skip the code that does AC97 vendor detection */
5340c0d06caSMauro Carvalho Chehab 		dev->audio_mode.ac97 = EM28XX_NO_AC97;
5350c0d06caSMauro Carvalho Chehab 		goto init_audio;
536920f1e4aSFrank Schaefer 	} else {
537920f1e4aSFrank Schaefer 		dev->int_audio_type = EM28XX_INT_AUDIO_AC97;
5380c0d06caSMauro Carvalho Chehab 	}
5390c0d06caSMauro Carvalho Chehab 
5400c0d06caSMauro Carvalho Chehab 	dev->audio_mode.ac97 = EM28XX_AC97_OTHER;
5410c0d06caSMauro Carvalho Chehab 
5420c0d06caSMauro Carvalho Chehab 	vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1);
5430c0d06caSMauro Carvalho Chehab 	if (vid1 < 0) {
5440c0d06caSMauro Carvalho Chehab 		/*
5450c0d06caSMauro Carvalho Chehab 		 * Device likely doesn't support AC97
5460c0d06caSMauro Carvalho Chehab 		 * Note: (some) em2800 devices without eeprom reports 0x91 on
5470c0d06caSMauro Carvalho Chehab 		 *	 CHIPCFG register, even not having an AC97 chip
5480c0d06caSMauro Carvalho Chehab 		 */
54929b05e22SMauro Carvalho Chehab 		dev_warn(&dev->intf->dev,
550ce8591ffSMauro Carvalho Chehab 			 "AC97 chip type couldn't be determined\n");
5510c0d06caSMauro Carvalho Chehab 		dev->audio_mode.ac97 = EM28XX_NO_AC97;
552c5874208SFrank Schaefer 		if (dev->usb_audio_type == EM28XX_USB_AUDIO_VENDOR)
553c5874208SFrank Schaefer 			dev->usb_audio_type = EM28XX_USB_AUDIO_NONE;
554920f1e4aSFrank Schaefer 		dev->int_audio_type = EM28XX_INT_AUDIO_NONE;
5550c0d06caSMauro Carvalho Chehab 		goto init_audio;
5560c0d06caSMauro Carvalho Chehab 	}
5570c0d06caSMauro Carvalho Chehab 
5580c0d06caSMauro Carvalho Chehab 	vid2 = em28xx_read_ac97(dev, AC97_VENDOR_ID2);
5590c0d06caSMauro Carvalho Chehab 	if (vid2 < 0)
5600c0d06caSMauro Carvalho Chehab 		goto init_audio;
5610c0d06caSMauro Carvalho Chehab 
5620c0d06caSMauro Carvalho Chehab 	vid = vid1 << 16 | vid2;
56329b05e22SMauro Carvalho Chehab 	dev_warn(&dev->intf->dev, "AC97 vendor ID = 0x%08x\n", vid);
5640c0d06caSMauro Carvalho Chehab 
5650c0d06caSMauro Carvalho Chehab 	feat = em28xx_read_ac97(dev, AC97_RESET);
5660c0d06caSMauro Carvalho Chehab 	if (feat < 0)
5670c0d06caSMauro Carvalho Chehab 		goto init_audio;
5680c0d06caSMauro Carvalho Chehab 
56929b05e22SMauro Carvalho Chehab 	dev_warn(&dev->intf->dev, "AC97 features = 0x%04x\n", feat);
5700c0d06caSMauro Carvalho Chehab 
5710c0d06caSMauro Carvalho Chehab 	/* Try to identify what audio processor we have */
572349ac5bbSMauro Carvalho Chehab 	if ((vid == 0xffffffff || vid == 0x83847650) && feat == 0x6a90)
5730c0d06caSMauro Carvalho Chehab 		dev->audio_mode.ac97 = EM28XX_AC97_EM202;
5740c0d06caSMauro Carvalho Chehab 	else if ((vid >> 8) == 0x838476)
5750c0d06caSMauro Carvalho Chehab 		dev->audio_mode.ac97 = EM28XX_AC97_SIGMATEL;
5760c0d06caSMauro Carvalho Chehab 
5770c0d06caSMauro Carvalho Chehab init_audio:
5780c0d06caSMauro Carvalho Chehab 	/* Reports detected AC97 processor */
5790c0d06caSMauro Carvalho Chehab 	switch (dev->audio_mode.ac97) {
5800c0d06caSMauro Carvalho Chehab 	case EM28XX_NO_AC97:
58129b05e22SMauro Carvalho Chehab 		dev_info(&dev->intf->dev, "No AC97 audio processor\n");
5820c0d06caSMauro Carvalho Chehab 		break;
5830c0d06caSMauro Carvalho Chehab 	case EM28XX_AC97_EM202:
58429b05e22SMauro Carvalho Chehab 		dev_info(&dev->intf->dev,
585ce8591ffSMauro Carvalho Chehab 			 "Empia 202 AC97 audio processor detected\n");
5860c0d06caSMauro Carvalho Chehab 		break;
5870c0d06caSMauro Carvalho Chehab 	case EM28XX_AC97_SIGMATEL:
58829b05e22SMauro Carvalho Chehab 		dev_info(&dev->intf->dev,
589ce8591ffSMauro Carvalho Chehab 			 "Sigmatel audio processor detected (stac 97%02x)\n",
59060a24ba0SFrank Schaefer 			 vid & 0xff);
5910c0d06caSMauro Carvalho Chehab 		break;
5920c0d06caSMauro Carvalho Chehab 	case EM28XX_AC97_OTHER:
59329b05e22SMauro Carvalho Chehab 		dev_warn(&dev->intf->dev,
594ce8591ffSMauro Carvalho Chehab 			 "Unknown AC97 audio processor detected!\n");
5950c0d06caSMauro Carvalho Chehab 		break;
5960c0d06caSMauro Carvalho Chehab 	default:
5970c0d06caSMauro Carvalho Chehab 		break;
5980c0d06caSMauro Carvalho Chehab 	}
5990c0d06caSMauro Carvalho Chehab 
6000c0d06caSMauro Carvalho Chehab 	return em28xx_audio_analog_set(dev);
6010c0d06caSMauro Carvalho Chehab }
6020c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(em28xx_audio_setup);
6030c0d06caSMauro Carvalho Chehab 
em28xx_find_led(struct em28xx * dev,enum em28xx_led_role role)6046b8a3170SFrank Schaefer const struct em28xx_led *em28xx_find_led(struct em28xx *dev,
6056b8a3170SFrank Schaefer 					 enum em28xx_led_role role)
6066b8a3170SFrank Schaefer {
6076b8a3170SFrank Schaefer 	if (dev->board.leds) {
6086b8a3170SFrank Schaefer 		u8 k = 0;
609fdf1bc9fSMauro Carvalho Chehab 
6106b8a3170SFrank Schaefer 		while (dev->board.leds[k].role >= 0 &&
6116b8a3170SFrank Schaefer 		       dev->board.leds[k].role < EM28XX_NUM_LED_ROLES) {
6126b8a3170SFrank Schaefer 			if (dev->board.leds[k].role == role)
6136b8a3170SFrank Schaefer 				return &dev->board.leds[k];
6146b8a3170SFrank Schaefer 			k++;
6156b8a3170SFrank Schaefer 		}
6166b8a3170SFrank Schaefer 	}
6176b8a3170SFrank Schaefer 	return NULL;
6186b8a3170SFrank Schaefer }
6196b8a3170SFrank Schaefer EXPORT_SYMBOL_GPL(em28xx_find_led);
6206b8a3170SFrank Schaefer 
em28xx_capture_start(struct em28xx * dev,int start)6210c0d06caSMauro Carvalho Chehab int em28xx_capture_start(struct em28xx *dev, int start)
6220c0d06caSMauro Carvalho Chehab {
6230c0d06caSMauro Carvalho Chehab 	int rc;
62454e92549SMauro Carvalho Chehab 	const struct em28xx_led *led = NULL;
6250c0d06caSMauro Carvalho Chehab 
6260c0d06caSMauro Carvalho Chehab 	if (dev->chip_id == CHIP_ID_EM2874 ||
6270c0d06caSMauro Carvalho Chehab 	    dev->chip_id == CHIP_ID_EM2884 ||
6289f1d0bdaSAntti Palosaari 	    dev->chip_id == CHIP_ID_EM28174 ||
6299f1d0bdaSAntti Palosaari 	    dev->chip_id == CHIP_ID_EM28178) {
6300c0d06caSMauro Carvalho Chehab 		/* The Transport Stream Enable Register moved in em2874 */
6311b5f69f5SBrad Love 		if (dev->dvb_xfer_bulk) {
6321b5f69f5SBrad Love 			/* Max Tx Size = 188 * 256 = 48128 - LCM(188,512) * 2 */
6331b5f69f5SBrad Love 			em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ?
6341b5f69f5SBrad Love 					 EM2874_R5D_TS1_PKT_SIZE :
6351b5f69f5SBrad Love 					 EM2874_R5E_TS2_PKT_SIZE,
6366b234c98SBrad Love 					 0xff);
6371b5f69f5SBrad Love 		} else {
6381b5f69f5SBrad Love 			/* ISOC Maximum Transfer Size = 188 * 5 */
6391b5f69f5SBrad Love 			em28xx_write_reg(dev, (dev->ts == PRIMARY_TS) ?
6401b5f69f5SBrad Love 					 EM2874_R5D_TS1_PKT_SIZE :
6411b5f69f5SBrad Love 					 EM2874_R5E_TS2_PKT_SIZE,
6421b5f69f5SBrad Love 					 dev->dvb_max_pkt_size_isoc / 188);
6431b5f69f5SBrad Love 		}
644be7fd3c3SBrad Love 		if (dev->ts == PRIMARY_TS)
645be7fd3c3SBrad Love 			rc = em28xx_write_reg_bits(dev,
646be7fd3c3SBrad Love 						   EM2874_R5F_TS_ENABLE,
647349ac5bbSMauro Carvalho Chehab 						   start ? EM2874_TS1_CAPTURE_ENABLE : 0x00,
648157eb9a0SRobert Schlabbach 						   EM2874_TS1_CAPTURE_ENABLE | EM2874_TS1_FILTER_ENABLE | EM2874_TS1_NULL_DISCARD);
649be7fd3c3SBrad Love 		else
650be7fd3c3SBrad Love 			rc = em28xx_write_reg_bits(dev,
651be7fd3c3SBrad Love 						   EM2874_R5F_TS_ENABLE,
652349ac5bbSMauro Carvalho Chehab 						   start ? EM2874_TS2_CAPTURE_ENABLE : 0x00,
653157eb9a0SRobert Schlabbach 						   EM2874_TS2_CAPTURE_ENABLE | EM2874_TS2_FILTER_ENABLE | EM2874_TS2_NULL_DISCARD);
65407e4de30SFrank Schaefer 	} else {
6550c0d06caSMauro Carvalho Chehab 		/* FIXME: which is the best order? */
6560c0d06caSMauro Carvalho Chehab 		/* video registers are sampled by VREF */
6570c0d06caSMauro Carvalho Chehab 		rc = em28xx_write_reg_bits(dev, EM28XX_R0C_USBSUSP,
6580c0d06caSMauro Carvalho Chehab 					   start ? 0x10 : 0x00, 0x10);
6590c0d06caSMauro Carvalho Chehab 		if (rc < 0)
6600c0d06caSMauro Carvalho Chehab 			return rc;
6610c0d06caSMauro Carvalho Chehab 
66207e4de30SFrank Schaefer 		if (start) {
663aa62980bSMauro Carvalho Chehab 			if (dev->is_webcam)
6640c0d06caSMauro Carvalho Chehab 				rc = em28xx_write_reg(dev, 0x13, 0x0c);
6650c0d06caSMauro Carvalho Chehab 
66607e4de30SFrank Schaefer 			/* Enable video capture */
6670c0d06caSMauro Carvalho Chehab 			rc = em28xx_write_reg(dev, 0x48, 0x00);
66854e92549SMauro Carvalho Chehab 			if (rc < 0)
66954e92549SMauro Carvalho Chehab 				return rc;
6700c0d06caSMauro Carvalho Chehab 
6710c0d06caSMauro Carvalho Chehab 			if (dev->mode == EM28XX_ANALOG_MODE)
67207e4de30SFrank Schaefer 				rc = em28xx_write_reg(dev,
673fdf1bc9fSMauro Carvalho Chehab 						      EM28XX_R12_VINENABLE,
674fdf1bc9fSMauro Carvalho Chehab 						      0x67);
6750c0d06caSMauro Carvalho Chehab 			else
67607e4de30SFrank Schaefer 				rc = em28xx_write_reg(dev,
677fdf1bc9fSMauro Carvalho Chehab 						      EM28XX_R12_VINENABLE,
678fdf1bc9fSMauro Carvalho Chehab 						      0x37);
67954e92549SMauro Carvalho Chehab 			if (rc < 0)
68054e92549SMauro Carvalho Chehab 				return rc;
6810c0d06caSMauro Carvalho Chehab 
682349ac5bbSMauro Carvalho Chehab 			usleep_range(10000, 11000);
68307e4de30SFrank Schaefer 		} else {
68407e4de30SFrank Schaefer 			/* disable video capture */
68507e4de30SFrank Schaefer 			rc = em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x27);
68607e4de30SFrank Schaefer 		}
68707e4de30SFrank Schaefer 	}
68807e4de30SFrank Schaefer 
68954e92549SMauro Carvalho Chehab 	if (dev->mode == EM28XX_ANALOG_MODE)
6906b8a3170SFrank Schaefer 		led = em28xx_find_led(dev, EM28XX_LED_ANALOG_CAPTURING);
691688e2dd4SBrad Love 	else if (dev->ts == PRIMARY_TS)
69254e92549SMauro Carvalho Chehab 		led = em28xx_find_led(dev, EM28XX_LED_DIGITAL_CAPTURING);
693688e2dd4SBrad Love 	else
694688e2dd4SBrad Love 		led = em28xx_find_led(dev, EM28XX_LED_DIGITAL_CAPTURING_TS2);
69554e92549SMauro Carvalho Chehab 
6966b8a3170SFrank Schaefer 	if (led)
69707e4de30SFrank Schaefer 		em28xx_write_reg_bits(dev, led->gpio_reg,
69807e4de30SFrank Schaefer 				      (!start ^ led->inverted) ?
69907e4de30SFrank Schaefer 				      ~led->gpio_mask : led->gpio_mask,
70007e4de30SFrank Schaefer 				      led->gpio_mask);
7010c0d06caSMauro Carvalho Chehab 
7020c0d06caSMauro Carvalho Chehab 	return rc;
7030c0d06caSMauro Carvalho Chehab }
7040c0d06caSMauro Carvalho Chehab 
em28xx_gpio_set(struct em28xx * dev,const struct em28xx_reg_seq * gpio)7050108ae7fSMauro Carvalho Chehab int em28xx_gpio_set(struct em28xx *dev, const struct em28xx_reg_seq *gpio)
7060c0d06caSMauro Carvalho Chehab {
7070c0d06caSMauro Carvalho Chehab 	int rc = 0;
7080c0d06caSMauro Carvalho Chehab 
7090c0d06caSMauro Carvalho Chehab 	if (!gpio)
7100c0d06caSMauro Carvalho Chehab 		return rc;
7110c0d06caSMauro Carvalho Chehab 
7120c0d06caSMauro Carvalho Chehab 	if (dev->mode != EM28XX_SUSPEND) {
7130c0d06caSMauro Carvalho Chehab 		em28xx_write_reg(dev, 0x48, 0x00);
7140c0d06caSMauro Carvalho Chehab 		if (dev->mode == EM28XX_ANALOG_MODE)
7150c0d06caSMauro Carvalho Chehab 			em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
7160c0d06caSMauro Carvalho Chehab 		else
7170c0d06caSMauro Carvalho Chehab 			em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
718349ac5bbSMauro Carvalho Chehab 		usleep_range(10000, 11000);
7190c0d06caSMauro Carvalho Chehab 	}
7200c0d06caSMauro Carvalho Chehab 
7210c0d06caSMauro Carvalho Chehab 	/* Send GPIO reset sequences specified at board entry */
7220c0d06caSMauro Carvalho Chehab 	while (gpio->sleep >= 0) {
7230c0d06caSMauro Carvalho Chehab 		if (gpio->reg >= 0) {
7240c0d06caSMauro Carvalho Chehab 			rc = em28xx_write_reg_bits(dev,
7250c0d06caSMauro Carvalho Chehab 						   gpio->reg,
7260c0d06caSMauro Carvalho Chehab 						   gpio->val,
7270c0d06caSMauro Carvalho Chehab 						   gpio->mask);
7280c0d06caSMauro Carvalho Chehab 			if (rc < 0)
7290c0d06caSMauro Carvalho Chehab 				return rc;
7300c0d06caSMauro Carvalho Chehab 		}
7310c0d06caSMauro Carvalho Chehab 		if (gpio->sleep > 0)
7320c0d06caSMauro Carvalho Chehab 			msleep(gpio->sleep);
7330c0d06caSMauro Carvalho Chehab 
7340c0d06caSMauro Carvalho Chehab 		gpio++;
7350c0d06caSMauro Carvalho Chehab 	}
7360c0d06caSMauro Carvalho Chehab 	return rc;
7370c0d06caSMauro Carvalho Chehab }
7380c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(em28xx_gpio_set);
7390c0d06caSMauro Carvalho Chehab 
em28xx_set_mode(struct em28xx * dev,enum em28xx_mode set_mode)7400c0d06caSMauro Carvalho Chehab int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode)
7410c0d06caSMauro Carvalho Chehab {
7420c0d06caSMauro Carvalho Chehab 	if (dev->mode == set_mode)
7430c0d06caSMauro Carvalho Chehab 		return 0;
7440c0d06caSMauro Carvalho Chehab 
7450c0d06caSMauro Carvalho Chehab 	if (set_mode == EM28XX_SUSPEND) {
7460c0d06caSMauro Carvalho Chehab 		dev->mode = set_mode;
7470c0d06caSMauro Carvalho Chehab 
7480c0d06caSMauro Carvalho Chehab 		/* FIXME: add suspend support for ac97 */
7490c0d06caSMauro Carvalho Chehab 
7500c0d06caSMauro Carvalho Chehab 		return em28xx_gpio_set(dev, dev->board.suspend_gpio);
7510c0d06caSMauro Carvalho Chehab 	}
7520c0d06caSMauro Carvalho Chehab 
7530c0d06caSMauro Carvalho Chehab 	dev->mode = set_mode;
7540c0d06caSMauro Carvalho Chehab 
7550c0d06caSMauro Carvalho Chehab 	if (dev->mode == EM28XX_DIGITAL_MODE)
7560c0d06caSMauro Carvalho Chehab 		return em28xx_gpio_set(dev, dev->board.dvb_gpio);
7570c0d06caSMauro Carvalho Chehab 	else
7580c0d06caSMauro Carvalho Chehab 		return em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio);
7590c0d06caSMauro Carvalho Chehab }
7600c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(em28xx_set_mode);
7610c0d06caSMauro Carvalho Chehab 
762349ac5bbSMauro Carvalho Chehab /*
763349ac5bbSMauro Carvalho Chehab  *URB control
764349ac5bbSMauro Carvalho Chehab  */
7650c0d06caSMauro Carvalho Chehab 
7660c0d06caSMauro Carvalho Chehab /*
767836e93bfSFrank Schaefer  * URB completion handler for isoc/bulk transfers
7680c0d06caSMauro Carvalho Chehab  */
em28xx_irq_callback(struct urb * urb)7690c0d06caSMauro Carvalho Chehab static void em28xx_irq_callback(struct urb *urb)
7700c0d06caSMauro Carvalho Chehab {
7710c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = urb->context;
772273925c7SSebastian Andrzej Siewior 	unsigned long flags;
7730c0d06caSMauro Carvalho Chehab 	int i;
7740c0d06caSMauro Carvalho Chehab 
7750c0d06caSMauro Carvalho Chehab 	switch (urb->status) {
7760c0d06caSMauro Carvalho Chehab 	case 0:             /* success */
7770c0d06caSMauro Carvalho Chehab 	case -ETIMEDOUT:    /* NAK */
7780c0d06caSMauro Carvalho Chehab 		break;
7790c0d06caSMauro Carvalho Chehab 	case -ECONNRESET:   /* kill */
7800c0d06caSMauro Carvalho Chehab 	case -ENOENT:
7810c0d06caSMauro Carvalho Chehab 	case -ESHUTDOWN:
7820c0d06caSMauro Carvalho Chehab 		return;
7830c0d06caSMauro Carvalho Chehab 	default:            /* error */
784349ac5bbSMauro Carvalho Chehab 		em28xx_isocdbg("urb completion error %d.\n", urb->status);
7850c0d06caSMauro Carvalho Chehab 		break;
7860c0d06caSMauro Carvalho Chehab 	}
7870c0d06caSMauro Carvalho Chehab 
7880c0d06caSMauro Carvalho Chehab 	/* Copy data from URB */
789273925c7SSebastian Andrzej Siewior 	spin_lock_irqsave(&dev->slock, flags);
79074209dc0SFrank Schaefer 	dev->usb_ctl.urb_data_copy(dev, urb);
791273925c7SSebastian Andrzej Siewior 	spin_unlock_irqrestore(&dev->slock, flags);
7920c0d06caSMauro Carvalho Chehab 
7930c0d06caSMauro Carvalho Chehab 	/* Reset urb buffers */
7940c0d06caSMauro Carvalho Chehab 	for (i = 0; i < urb->number_of_packets; i++) {
795836e93bfSFrank Schaefer 		/* isoc only (bulk: number_of_packets = 0) */
7960c0d06caSMauro Carvalho Chehab 		urb->iso_frame_desc[i].status = 0;
7970c0d06caSMauro Carvalho Chehab 		urb->iso_frame_desc[i].actual_length = 0;
7980c0d06caSMauro Carvalho Chehab 	}
7990c0d06caSMauro Carvalho Chehab 	urb->status = 0;
8000c0d06caSMauro Carvalho Chehab 
8010c0d06caSMauro Carvalho Chehab 	urb->status = usb_submit_urb(urb, GFP_ATOMIC);
8020c0d06caSMauro Carvalho Chehab 	if (urb->status) {
8030c0d06caSMauro Carvalho Chehab 		em28xx_isocdbg("urb resubmit failed (error=%i)\n",
8040c0d06caSMauro Carvalho Chehab 			       urb->status);
8050c0d06caSMauro Carvalho Chehab 	}
8060c0d06caSMauro Carvalho Chehab }
8070c0d06caSMauro Carvalho Chehab 
8080c0d06caSMauro Carvalho Chehab /*
8090c0d06caSMauro Carvalho Chehab  * Stop and Deallocate URBs
8100c0d06caSMauro Carvalho Chehab  */
em28xx_uninit_usb_xfer(struct em28xx * dev,enum em28xx_mode mode)811afb177e0SFrank Schaefer void em28xx_uninit_usb_xfer(struct em28xx *dev, enum em28xx_mode mode)
8120c0d06caSMauro Carvalho Chehab {
8130c0d06caSMauro Carvalho Chehab 	struct urb *urb;
814afb177e0SFrank Schaefer 	struct em28xx_usb_bufs *usb_bufs;
8150c0d06caSMauro Carvalho Chehab 	int i;
8160c0d06caSMauro Carvalho Chehab 
817349ac5bbSMauro Carvalho Chehab 	em28xx_isocdbg("called %s in mode %d\n", __func__, mode);
8180c0d06caSMauro Carvalho Chehab 
8190c0d06caSMauro Carvalho Chehab 	if (mode == EM28XX_DIGITAL_MODE)
820afb177e0SFrank Schaefer 		usb_bufs = &dev->usb_ctl.digital_bufs;
8210c0d06caSMauro Carvalho Chehab 	else
822afb177e0SFrank Schaefer 		usb_bufs = &dev->usb_ctl.analog_bufs;
8230c0d06caSMauro Carvalho Chehab 
824afb177e0SFrank Schaefer 	for (i = 0; i < usb_bufs->num_bufs; i++) {
825afb177e0SFrank Schaefer 		urb = usb_bufs->urb[i];
8260c0d06caSMauro Carvalho Chehab 		if (urb) {
8270c0d06caSMauro Carvalho Chehab 			if (!irqs_disabled())
8280c0d06caSMauro Carvalho Chehab 				usb_kill_urb(urb);
8290c0d06caSMauro Carvalho Chehab 			else
8300c0d06caSMauro Carvalho Chehab 				usb_unlink_urb(urb);
8310c0d06caSMauro Carvalho Chehab 
8320c0d06caSMauro Carvalho Chehab 			usb_free_urb(urb);
833afb177e0SFrank Schaefer 			usb_bufs->urb[i] = NULL;
8340c0d06caSMauro Carvalho Chehab 		}
8350c0d06caSMauro Carvalho Chehab 	}
8360c0d06caSMauro Carvalho Chehab 
837afb177e0SFrank Schaefer 	kfree(usb_bufs->urb);
838d571b592SMauro Carvalho Chehab 	kfree(usb_bufs->buf);
8390c0d06caSMauro Carvalho Chehab 
840afb177e0SFrank Schaefer 	usb_bufs->urb = NULL;
841d571b592SMauro Carvalho Chehab 	usb_bufs->buf = NULL;
842afb177e0SFrank Schaefer 	usb_bufs->num_bufs = 0;
8430c0d06caSMauro Carvalho Chehab 
8440c0d06caSMauro Carvalho Chehab 	em28xx_capture_start(dev, 0);
8450c0d06caSMauro Carvalho Chehab }
846afb177e0SFrank Schaefer EXPORT_SYMBOL_GPL(em28xx_uninit_usb_xfer);
8470c0d06caSMauro Carvalho Chehab 
8480c0d06caSMauro Carvalho Chehab /*
8490c0d06caSMauro Carvalho Chehab  * Stop URBs
8500c0d06caSMauro Carvalho Chehab  */
em28xx_stop_urbs(struct em28xx * dev)8510c0d06caSMauro Carvalho Chehab void em28xx_stop_urbs(struct em28xx *dev)
8520c0d06caSMauro Carvalho Chehab {
8530c0d06caSMauro Carvalho Chehab 	int i;
8540c0d06caSMauro Carvalho Chehab 	struct urb *urb;
85574209dc0SFrank Schaefer 	struct em28xx_usb_bufs *isoc_bufs = &dev->usb_ctl.digital_bufs;
8560c0d06caSMauro Carvalho Chehab 
857349ac5bbSMauro Carvalho Chehab 	em28xx_isocdbg("called %s\n", __func__);
8580c0d06caSMauro Carvalho Chehab 
8590c0d06caSMauro Carvalho Chehab 	for (i = 0; i < isoc_bufs->num_bufs; i++) {
8600c0d06caSMauro Carvalho Chehab 		urb = isoc_bufs->urb[i];
8610c0d06caSMauro Carvalho Chehab 		if (urb) {
8620c0d06caSMauro Carvalho Chehab 			if (!irqs_disabled())
8630c0d06caSMauro Carvalho Chehab 				usb_kill_urb(urb);
8640c0d06caSMauro Carvalho Chehab 			else
8650c0d06caSMauro Carvalho Chehab 				usb_unlink_urb(urb);
8660c0d06caSMauro Carvalho Chehab 		}
8670c0d06caSMauro Carvalho Chehab 	}
8680c0d06caSMauro Carvalho Chehab 
8690c0d06caSMauro Carvalho Chehab 	em28xx_capture_start(dev, 0);
8700c0d06caSMauro Carvalho Chehab }
8710c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL_GPL(em28xx_stop_urbs);
8720c0d06caSMauro Carvalho Chehab 
8730c0d06caSMauro Carvalho Chehab /*
8740c0d06caSMauro Carvalho Chehab  * Allocate URBs
8750c0d06caSMauro Carvalho Chehab  */
em28xx_alloc_urbs(struct em28xx * dev,enum em28xx_mode mode,int xfer_bulk,int num_bufs,int max_pkt_size,int packet_multiplier)8766ddd89d0SFrank Schaefer int em28xx_alloc_urbs(struct em28xx *dev, enum em28xx_mode mode, int xfer_bulk,
8776ddd89d0SFrank Schaefer 		      int num_bufs, int max_pkt_size, int packet_multiplier)
8780c0d06caSMauro Carvalho Chehab {
8796ddd89d0SFrank Schaefer 	struct em28xx_usb_bufs *usb_bufs;
880c6d48134SMauro Carvalho Chehab 	struct urb *urb;
881c6d48134SMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(dev->intf);
8820c0d06caSMauro Carvalho Chehab 	int i;
8830c0d06caSMauro Carvalho Chehab 	int sb_size, pipe;
8840c0d06caSMauro Carvalho Chehab 	int j, k;
8850c0d06caSMauro Carvalho Chehab 
886349ac5bbSMauro Carvalho Chehab 	em28xx_isocdbg("em28xx: called %s in mode %d\n", __func__, mode);
8870c0d06caSMauro Carvalho Chehab 
888349ac5bbSMauro Carvalho Chehab 	/*
889349ac5bbSMauro Carvalho Chehab 	 * Check mode and if we have an endpoint for the selected
890349ac5bbSMauro Carvalho Chehab 	 * transfer type, select buffer
891349ac5bbSMauro Carvalho Chehab 	 */
892c647a91aSFrank Schaefer 	if (mode == EM28XX_DIGITAL_MODE) {
893c647a91aSFrank Schaefer 		if ((xfer_bulk && !dev->dvb_ep_bulk) ||
894c647a91aSFrank Schaefer 		    (!xfer_bulk && !dev->dvb_ep_isoc)) {
89529b05e22SMauro Carvalho Chehab 			dev_err(&dev->intf->dev,
896ce8591ffSMauro Carvalho Chehab 				"no endpoint for DVB mode and transfer type %d\n",
897c647a91aSFrank Schaefer 				xfer_bulk > 0);
898c647a91aSFrank Schaefer 			return -EINVAL;
899c647a91aSFrank Schaefer 		}
9006ddd89d0SFrank Schaefer 		usb_bufs = &dev->usb_ctl.digital_bufs;
901c647a91aSFrank Schaefer 	} else if (mode == EM28XX_ANALOG_MODE) {
902c647a91aSFrank Schaefer 		if ((xfer_bulk && !dev->analog_ep_bulk) ||
903c647a91aSFrank Schaefer 		    (!xfer_bulk && !dev->analog_ep_isoc)) {
90429b05e22SMauro Carvalho Chehab 			dev_err(&dev->intf->dev,
905ce8591ffSMauro Carvalho Chehab 				"no endpoint for analog mode and transfer type %d\n",
906c647a91aSFrank Schaefer 				xfer_bulk > 0);
907c647a91aSFrank Schaefer 			return -EINVAL;
908c647a91aSFrank Schaefer 		}
9096ddd89d0SFrank Schaefer 		usb_bufs = &dev->usb_ctl.analog_bufs;
910c647a91aSFrank Schaefer 	} else {
91129b05e22SMauro Carvalho Chehab 		dev_err(&dev->intf->dev, "invalid mode selected\n");
912c647a91aSFrank Schaefer 		return -EINVAL;
913c647a91aSFrank Schaefer 	}
9140c0d06caSMauro Carvalho Chehab 
9150c0d06caSMauro Carvalho Chehab 	/* De-allocates all pending stuff */
916afb177e0SFrank Schaefer 	em28xx_uninit_usb_xfer(dev, mode);
9170c0d06caSMauro Carvalho Chehab 
9186ddd89d0SFrank Schaefer 	usb_bufs->num_bufs = num_bufs;
9190c0d06caSMauro Carvalho Chehab 
920349ac5bbSMauro Carvalho Chehab 	usb_bufs->urb = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
9218314d402SMauro Carvalho Chehab 	if (!usb_bufs->urb)
9220c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
9230c0d06caSMauro Carvalho Chehab 
924349ac5bbSMauro Carvalho Chehab 	usb_bufs->buf = kcalloc(num_bufs, sizeof(void *), GFP_KERNEL);
925d571b592SMauro Carvalho Chehab 	if (!usb_bufs->buf) {
926ecbce48fSMarkus Elfring 		kfree(usb_bufs->urb);
9270c0d06caSMauro Carvalho Chehab 		return -ENOMEM;
9280c0d06caSMauro Carvalho Chehab 	}
9290c0d06caSMauro Carvalho Chehab 
9306ddd89d0SFrank Schaefer 	usb_bufs->max_pkt_size = max_pkt_size;
9316ddd89d0SFrank Schaefer 	if (xfer_bulk)
9326ddd89d0SFrank Schaefer 		usb_bufs->num_packets = 0;
9336ddd89d0SFrank Schaefer 	else
9346ddd89d0SFrank Schaefer 		usb_bufs->num_packets = packet_multiplier;
93574209dc0SFrank Schaefer 	dev->usb_ctl.vid_buf = NULL;
93674209dc0SFrank Schaefer 	dev->usb_ctl.vbi_buf = NULL;
9370c0d06caSMauro Carvalho Chehab 
9386ddd89d0SFrank Schaefer 	sb_size = packet_multiplier * usb_bufs->max_pkt_size;
9390c0d06caSMauro Carvalho Chehab 
9400c0d06caSMauro Carvalho Chehab 	/* allocate urbs and transfer buffers */
9416ddd89d0SFrank Schaefer 	for (i = 0; i < usb_bufs->num_bufs; i++) {
9426ddd89d0SFrank Schaefer 		urb = usb_alloc_urb(usb_bufs->num_packets, GFP_KERNEL);
9430c0d06caSMauro Carvalho Chehab 		if (!urb) {
944afb177e0SFrank Schaefer 			em28xx_uninit_usb_xfer(dev, mode);
9450c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
9460c0d06caSMauro Carvalho Chehab 		}
9476ddd89d0SFrank Schaefer 		usb_bufs->urb[i] = urb;
9480c0d06caSMauro Carvalho Chehab 
949d571b592SMauro Carvalho Chehab 		usb_bufs->buf[i] = kzalloc(sb_size, GFP_KERNEL);
950d571b592SMauro Carvalho Chehab 		if (!usb_bufs->buf[i]) {
951d571b592SMauro Carvalho Chehab 			for (i--; i >= 0; i--)
952d571b592SMauro Carvalho Chehab 				kfree(usb_bufs->buf[i]);
953d571b592SMauro Carvalho Chehab 
954a26efd19SDinghao Liu 			em28xx_uninit_usb_xfer(dev, mode);
9550c0d06caSMauro Carvalho Chehab 			return -ENOMEM;
9560c0d06caSMauro Carvalho Chehab 		}
957d571b592SMauro Carvalho Chehab 
958d571b592SMauro Carvalho Chehab 		urb->transfer_flags = URB_FREE_BUFFER;
9590c0d06caSMauro Carvalho Chehab 
9606ddd89d0SFrank Schaefer 		if (xfer_bulk) { /* bulk */
961c6d48134SMauro Carvalho Chehab 			pipe = usb_rcvbulkpipe(udev,
9626ddd89d0SFrank Schaefer 					       mode == EM28XX_ANALOG_MODE ?
963c647a91aSFrank Schaefer 					       dev->analog_ep_bulk :
964c647a91aSFrank Schaefer 					       dev->dvb_ep_bulk);
965d571b592SMauro Carvalho Chehab 			usb_fill_bulk_urb(urb, udev, pipe, usb_bufs->buf[i],
966d571b592SMauro Carvalho Chehab 					  sb_size, em28xx_irq_callback, dev);
9676ddd89d0SFrank Schaefer 		} else { /* isoc */
968c6d48134SMauro Carvalho Chehab 			pipe = usb_rcvisocpipe(udev,
9690c0d06caSMauro Carvalho Chehab 					       mode == EM28XX_ANALOG_MODE ?
970c647a91aSFrank Schaefer 					       dev->analog_ep_isoc :
971c647a91aSFrank Schaefer 					       dev->dvb_ep_isoc);
972d571b592SMauro Carvalho Chehab 			usb_fill_int_urb(urb, udev, pipe, usb_bufs->buf[i],
973d571b592SMauro Carvalho Chehab 					 sb_size, em28xx_irq_callback, dev, 1);
974d571b592SMauro Carvalho Chehab 			urb->transfer_flags |= URB_ISO_ASAP;
9750c0d06caSMauro Carvalho Chehab 			k = 0;
9766ddd89d0SFrank Schaefer 			for (j = 0; j < usb_bufs->num_packets; j++) {
9770c0d06caSMauro Carvalho Chehab 				urb->iso_frame_desc[j].offset = k;
9780c0d06caSMauro Carvalho Chehab 				urb->iso_frame_desc[j].length =
9796ddd89d0SFrank Schaefer 							usb_bufs->max_pkt_size;
9806ddd89d0SFrank Schaefer 				k += usb_bufs->max_pkt_size;
9810c0d06caSMauro Carvalho Chehab 			}
9820c0d06caSMauro Carvalho Chehab 		}
9830c0d06caSMauro Carvalho Chehab 
9846ddd89d0SFrank Schaefer 		urb->number_of_packets = usb_bufs->num_packets;
9856ddd89d0SFrank Schaefer 	}
9866ddd89d0SFrank Schaefer 
9870c0d06caSMauro Carvalho Chehab 	return 0;
9880c0d06caSMauro Carvalho Chehab }
9896ddd89d0SFrank Schaefer EXPORT_SYMBOL_GPL(em28xx_alloc_urbs);
9900c0d06caSMauro Carvalho Chehab 
9910c0d06caSMauro Carvalho Chehab /*
9920c0d06caSMauro Carvalho Chehab  * Allocate URBs and start IRQ
9930c0d06caSMauro Carvalho Chehab  */
em28xx_init_usb_xfer(struct em28xx * dev,enum em28xx_mode mode,int xfer_bulk,int num_bufs,int max_pkt_size,int packet_multiplier,int (* urb_data_copy)(struct em28xx * dev,struct urb * urb))994057ca0daSFrank Schaefer int em28xx_init_usb_xfer(struct em28xx *dev, enum em28xx_mode mode,
995057ca0daSFrank Schaefer 			 int xfer_bulk, int num_bufs, int max_pkt_size,
996057ca0daSFrank Schaefer 		    int packet_multiplier,
997057ca0daSFrank Schaefer 		    int (*urb_data_copy)(struct em28xx *dev, struct urb *urb))
9980c0d06caSMauro Carvalho Chehab {
9990c0d06caSMauro Carvalho Chehab 	struct em28xx_dmaqueue *dma_q = &dev->vidq;
10000c0d06caSMauro Carvalho Chehab 	struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
1001057ca0daSFrank Schaefer 	struct em28xx_usb_bufs *usb_bufs;
1002c6d48134SMauro Carvalho Chehab 	struct usb_device *udev = interface_to_usbdev(dev->intf);
10030c0d06caSMauro Carvalho Chehab 	int i;
10040c0d06caSMauro Carvalho Chehab 	int rc;
10050c0d06caSMauro Carvalho Chehab 	int alloc;
10060c0d06caSMauro Carvalho Chehab 
1007349ac5bbSMauro Carvalho Chehab 	em28xx_isocdbg("em28xx: called %s in mode %d\n", __func__, mode);
10080c0d06caSMauro Carvalho Chehab 
1009057ca0daSFrank Schaefer 	dev->usb_ctl.urb_data_copy = urb_data_copy;
10100c0d06caSMauro Carvalho Chehab 
10110c0d06caSMauro Carvalho Chehab 	if (mode == EM28XX_DIGITAL_MODE) {
1012057ca0daSFrank Schaefer 		usb_bufs = &dev->usb_ctl.digital_bufs;
1013057ca0daSFrank Schaefer 		/* no need to free/alloc usb buffers in digital mode */
10140c0d06caSMauro Carvalho Chehab 		alloc = 0;
10150c0d06caSMauro Carvalho Chehab 	} else {
1016057ca0daSFrank Schaefer 		usb_bufs = &dev->usb_ctl.analog_bufs;
10170c0d06caSMauro Carvalho Chehab 		alloc = 1;
10180c0d06caSMauro Carvalho Chehab 	}
10190c0d06caSMauro Carvalho Chehab 
10200c0d06caSMauro Carvalho Chehab 	if (alloc) {
1021057ca0daSFrank Schaefer 		rc = em28xx_alloc_urbs(dev, mode, xfer_bulk, num_bufs,
1022057ca0daSFrank Schaefer 				       max_pkt_size, packet_multiplier);
10230c0d06caSMauro Carvalho Chehab 		if (rc)
10240c0d06caSMauro Carvalho Chehab 			return rc;
10250c0d06caSMauro Carvalho Chehab 	}
10260c0d06caSMauro Carvalho Chehab 
1027337fe8daSFrank Schaefer 	if (xfer_bulk) {
1028c6d48134SMauro Carvalho Chehab 		rc = usb_clear_halt(udev, usb_bufs->urb[0]->pipe);
1029337fe8daSFrank Schaefer 		if (rc < 0) {
103029b05e22SMauro Carvalho Chehab 			dev_err(&dev->intf->dev,
1031ce8591ffSMauro Carvalho Chehab 				"failed to clear USB bulk endpoint stall/halt condition (error=%i)\n",
1032337fe8daSFrank Schaefer 			       rc);
1033337fe8daSFrank Schaefer 			em28xx_uninit_usb_xfer(dev, mode);
1034337fe8daSFrank Schaefer 			return rc;
1035337fe8daSFrank Schaefer 		}
1036337fe8daSFrank Schaefer 	}
1037337fe8daSFrank Schaefer 
10380c0d06caSMauro Carvalho Chehab 	init_waitqueue_head(&dma_q->wq);
10390c0d06caSMauro Carvalho Chehab 	init_waitqueue_head(&vbi_dma_q->wq);
10400c0d06caSMauro Carvalho Chehab 
10410c0d06caSMauro Carvalho Chehab 	em28xx_capture_start(dev, 1);
10420c0d06caSMauro Carvalho Chehab 
10430c0d06caSMauro Carvalho Chehab 	/* submit urbs and enables IRQ */
1044057ca0daSFrank Schaefer 	for (i = 0; i < usb_bufs->num_bufs; i++) {
10452453e607SJia-Ju Bai 		rc = usb_submit_urb(usb_bufs->urb[i], GFP_KERNEL);
10460c0d06caSMauro Carvalho Chehab 		if (rc) {
104729b05e22SMauro Carvalho Chehab 			dev_err(&dev->intf->dev,
1048ce8591ffSMauro Carvalho Chehab 				"submit of urb %i failed (error=%i)\n", i, rc);
1049afb177e0SFrank Schaefer 			em28xx_uninit_usb_xfer(dev, mode);
10500c0d06caSMauro Carvalho Chehab 			return rc;
10510c0d06caSMauro Carvalho Chehab 		}
10520c0d06caSMauro Carvalho Chehab 	}
10530c0d06caSMauro Carvalho Chehab 
10540c0d06caSMauro Carvalho Chehab 	return 0;
10550c0d06caSMauro Carvalho Chehab }
1056057ca0daSFrank Schaefer EXPORT_SYMBOL_GPL(em28xx_init_usb_xfer);
10570c0d06caSMauro Carvalho Chehab 
10580c0d06caSMauro Carvalho Chehab /*
10590c0d06caSMauro Carvalho Chehab  * Device control list
10600c0d06caSMauro Carvalho Chehab  */
10610c0d06caSMauro Carvalho Chehab 
10620c0d06caSMauro Carvalho Chehab static LIST_HEAD(em28xx_devlist);
10630c0d06caSMauro Carvalho Chehab static DEFINE_MUTEX(em28xx_devlist_mutex);
10640c0d06caSMauro Carvalho Chehab 
10650c0d06caSMauro Carvalho Chehab /*
10660c0d06caSMauro Carvalho Chehab  * Extension interface
10670c0d06caSMauro Carvalho Chehab  */
10680c0d06caSMauro Carvalho Chehab 
10690c0d06caSMauro Carvalho Chehab static LIST_HEAD(em28xx_extension_devlist);
10700c0d06caSMauro Carvalho Chehab 
em28xx_register_extension(struct em28xx_ops * ops)10710c0d06caSMauro Carvalho Chehab int em28xx_register_extension(struct em28xx_ops *ops)
10720c0d06caSMauro Carvalho Chehab {
10730c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = NULL;
10740c0d06caSMauro Carvalho Chehab 
10750c0d06caSMauro Carvalho Chehab 	mutex_lock(&em28xx_devlist_mutex);
10760c0d06caSMauro Carvalho Chehab 	list_add_tail(&ops->next, &em28xx_extension_devlist);
10770c0d06caSMauro Carvalho Chehab 	list_for_each_entry(dev, &em28xx_devlist, devlist) {
1078be7fd3c3SBrad Love 		if (ops->init) {
10790c0d06caSMauro Carvalho Chehab 			ops->init(dev);
1080349ac5bbSMauro Carvalho Chehab 			if (dev->dev_next)
1081be7fd3c3SBrad Love 				ops->init(dev->dev_next);
1082be7fd3c3SBrad Love 		}
10830c0d06caSMauro Carvalho Chehab 	}
10840c0d06caSMauro Carvalho Chehab 	mutex_unlock(&em28xx_devlist_mutex);
10852a96f60eSMauro Carvalho Chehab 	pr_info("em28xx: Registered (%s) extension\n", ops->name);
10860c0d06caSMauro Carvalho Chehab 	return 0;
10870c0d06caSMauro Carvalho Chehab }
10880c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL(em28xx_register_extension);
10890c0d06caSMauro Carvalho Chehab 
em28xx_unregister_extension(struct em28xx_ops * ops)10900c0d06caSMauro Carvalho Chehab void em28xx_unregister_extension(struct em28xx_ops *ops)
10910c0d06caSMauro Carvalho Chehab {
10920c0d06caSMauro Carvalho Chehab 	struct em28xx *dev = NULL;
10930c0d06caSMauro Carvalho Chehab 
10940c0d06caSMauro Carvalho Chehab 	mutex_lock(&em28xx_devlist_mutex);
10950c0d06caSMauro Carvalho Chehab 	list_for_each_entry(dev, &em28xx_devlist, devlist) {
1096be7fd3c3SBrad Love 		if (ops->fini) {
1097349ac5bbSMauro Carvalho Chehab 			if (dev->dev_next)
1098be7fd3c3SBrad Love 				ops->fini(dev->dev_next);
10990c0d06caSMauro Carvalho Chehab 			ops->fini(dev);
11000c0d06caSMauro Carvalho Chehab 		}
1101be7fd3c3SBrad Love 	}
11020c0d06caSMauro Carvalho Chehab 	list_del(&ops->next);
11030c0d06caSMauro Carvalho Chehab 	mutex_unlock(&em28xx_devlist_mutex);
1104ce8591ffSMauro Carvalho Chehab 	pr_info("em28xx: Removed (%s) extension\n", ops->name);
11050c0d06caSMauro Carvalho Chehab }
11060c0d06caSMauro Carvalho Chehab EXPORT_SYMBOL(em28xx_unregister_extension);
11070c0d06caSMauro Carvalho Chehab 
em28xx_init_extension(struct em28xx * dev)11080c0d06caSMauro Carvalho Chehab void em28xx_init_extension(struct em28xx *dev)
11090c0d06caSMauro Carvalho Chehab {
11100c0d06caSMauro Carvalho Chehab 	const struct em28xx_ops *ops = NULL;
11110c0d06caSMauro Carvalho Chehab 
11120c0d06caSMauro Carvalho Chehab 	mutex_lock(&em28xx_devlist_mutex);
11130c0d06caSMauro Carvalho Chehab 	list_add_tail(&dev->devlist, &em28xx_devlist);
11140c0d06caSMauro Carvalho Chehab 	list_for_each_entry(ops, &em28xx_extension_devlist, next) {
1115be7fd3c3SBrad Love 		if (ops->init) {
11160c0d06caSMauro Carvalho Chehab 			ops->init(dev);
1117349ac5bbSMauro Carvalho Chehab 			if (dev->dev_next)
1118be7fd3c3SBrad Love 				ops->init(dev->dev_next);
1119be7fd3c3SBrad Love 		}
11200c0d06caSMauro Carvalho Chehab 	}
11210c0d06caSMauro Carvalho Chehab 	mutex_unlock(&em28xx_devlist_mutex);
11220c0d06caSMauro Carvalho Chehab }
11230c0d06caSMauro Carvalho Chehab 
em28xx_close_extension(struct em28xx * dev)11240c0d06caSMauro Carvalho Chehab void em28xx_close_extension(struct em28xx *dev)
11250c0d06caSMauro Carvalho Chehab {
11260c0d06caSMauro Carvalho Chehab 	const struct em28xx_ops *ops = NULL;
11270c0d06caSMauro Carvalho Chehab 
11280c0d06caSMauro Carvalho Chehab 	mutex_lock(&em28xx_devlist_mutex);
11290c0d06caSMauro Carvalho Chehab 	list_for_each_entry(ops, &em28xx_extension_devlist, next) {
1130be7fd3c3SBrad Love 		if (ops->fini) {
1131349ac5bbSMauro Carvalho Chehab 			if (dev->dev_next)
1132be7fd3c3SBrad Love 				ops->fini(dev->dev_next);
11330c0d06caSMauro Carvalho Chehab 			ops->fini(dev);
11340c0d06caSMauro Carvalho Chehab 		}
1135be7fd3c3SBrad Love 	}
11360c0d06caSMauro Carvalho Chehab 	list_del(&dev->devlist);
11370c0d06caSMauro Carvalho Chehab 	mutex_unlock(&em28xx_devlist_mutex);
11380c0d06caSMauro Carvalho Chehab }
11399c669b73SShuah Khan 
em28xx_suspend_extension(struct em28xx * dev)11409c669b73SShuah Khan int em28xx_suspend_extension(struct em28xx *dev)
11419c669b73SShuah Khan {
11429c669b73SShuah Khan 	const struct em28xx_ops *ops = NULL;
11439c669b73SShuah Khan 
114429b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Suspending extensions\n");
11459c669b73SShuah Khan 	mutex_lock(&em28xx_devlist_mutex);
11469c669b73SShuah Khan 	list_for_each_entry(ops, &em28xx_extension_devlist, next) {
114751fa3b70SColin Ian King 		if (!ops->suspend)
114851fa3b70SColin Ian King 			continue;
11499c669b73SShuah Khan 		ops->suspend(dev);
1150349ac5bbSMauro Carvalho Chehab 		if (dev->dev_next)
1151be7fd3c3SBrad Love 			ops->suspend(dev->dev_next);
11529c669b73SShuah Khan 	}
11539c669b73SShuah Khan 	mutex_unlock(&em28xx_devlist_mutex);
11549c669b73SShuah Khan 	return 0;
11559c669b73SShuah Khan }
11569c669b73SShuah Khan 
em28xx_resume_extension(struct em28xx * dev)11579c669b73SShuah Khan int em28xx_resume_extension(struct em28xx *dev)
11589c669b73SShuah Khan {
11599c669b73SShuah Khan 	const struct em28xx_ops *ops = NULL;
11609c669b73SShuah Khan 
116129b05e22SMauro Carvalho Chehab 	dev_info(&dev->intf->dev, "Resuming extensions\n");
11629c669b73SShuah Khan 	mutex_lock(&em28xx_devlist_mutex);
11639c669b73SShuah Khan 	list_for_each_entry(ops, &em28xx_extension_devlist, next) {
1164fd901b6eSMauro Carvalho Chehab 		if (!ops->resume)
1165fd901b6eSMauro Carvalho Chehab 			continue;
11669c669b73SShuah Khan 		ops->resume(dev);
1167349ac5bbSMauro Carvalho Chehab 		if (dev->dev_next)
1168be7fd3c3SBrad Love 			ops->resume(dev->dev_next);
11699c669b73SShuah Khan 	}
11709c669b73SShuah Khan 	mutex_unlock(&em28xx_devlist_mutex);
11719c669b73SShuah Khan 	return 0;
11729c669b73SShuah Khan }
1173