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