V4L/DVB (7615): em28xx: Provide the proper support for switching between analog/digital
Before this patch, HVR900/HVR950 were incorreclty going back to analog. The result is that only digital were working. This patch provides the proper setup for analog/digital and tuner callback. It also properly resets analog into a sane state at open(). Thanks to Steven Toth <stoth@linuxtv.org> and Michael Krufky <mkrufky@linuxtv.org> for helping to set the proper parameters to GPO/GPIO em2883 ports. Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
Родитель
82ac4f8765
Коммит
c67ec53f8f
|
@ -436,19 +436,25 @@ MODULE_DEVICE_TABLE(usb, em28xx_id_table);
|
|||
|
||||
/* Board Hauppauge WinTV HVR 900 analog */
|
||||
struct em28xx_reg_seq hauppauge_wintv_hvr_900_analog[] = {
|
||||
{ -1, -1, 6},
|
||||
{EM28XX_R08_GPIO, 0x2d, 10},
|
||||
{EM28XX_R08_GPIO, 0x3d, 5},
|
||||
{ -1, -1, -1},
|
||||
{EM28XX_R08_GPIO, 0x2d, ~EM_GPIO_4, 10},
|
||||
{0x05, 0xff, 0x10, 10},
|
||||
{ -1, -1, -1, -1},
|
||||
};
|
||||
|
||||
/* Board Hauppauge WinTV HVR 900 digital */
|
||||
struct em28xx_reg_seq hauppauge_wintv_hvr_900_digital[] = {
|
||||
{ -1, -1, 6},
|
||||
{EM28XX_R08_GPIO, 0x2e, 6},
|
||||
{EM28XX_R08_GPIO, 0x3e, 6},
|
||||
{EM2880_R04_GPO, 0x04, 10},
|
||||
{EM2880_R04_GPO, 0x0c, 10},
|
||||
{ -1, -1, -1},
|
||||
{EM28XX_R08_GPIO, 0x2e, ~EM_GPIO_4, 10},
|
||||
{EM2880_R04_GPO, 0x04, 0x0f, 10},
|
||||
{EM2880_R04_GPO, 0x0c, 0x0f, 10},
|
||||
{ -1, -1, -1, -1},
|
||||
};
|
||||
|
||||
/* Board Hauppauge WinTV HVR 900 tuner_callback */
|
||||
struct em28xx_reg_seq hauppauge_wintv_hvr_900_tuner_callback[] = {
|
||||
{EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
|
||||
{EM28XX_R08_GPIO, 0, EM_GPIO_4, 10},
|
||||
{EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10},
|
||||
{ -1, -1, -1, -1},
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -469,7 +475,6 @@ int em28xx_tuner_callback(void *ptr, int command, int arg)
|
|||
{
|
||||
int rc = 0;
|
||||
struct em28xx *dev = ptr;
|
||||
struct em28xx_reg_seq *gpio;
|
||||
|
||||
if (dev->tuner_type != TUNER_XC2028)
|
||||
return 0;
|
||||
|
@ -478,32 +483,10 @@ int em28xx_tuner_callback(void *ptr, int command, int arg)
|
|||
return 0;
|
||||
|
||||
if (dev->mode == EM28XX_ANALOG_MODE)
|
||||
gpio = dev->analog_gpio;
|
||||
rc = em28xx_gpio_set(dev, dev->tun_analog_gpio);
|
||||
else
|
||||
gpio = dev->digital_gpio;
|
||||
rc = em28xx_gpio_set(dev, dev->tun_digital_gpio);
|
||||
|
||||
/* djh - Not sure if these are still required */
|
||||
dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
|
||||
if (dev->mode == EM28XX_ANALOG_MODE)
|
||||
dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1);
|
||||
else
|
||||
dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x37", 1);
|
||||
msleep(6);
|
||||
|
||||
if (!gpio)
|
||||
return rc;
|
||||
|
||||
/* Send GPIO reset sequences specified at board entry */
|
||||
while (gpio->sleep >= 0) {
|
||||
if (gpio->reg >= 0)
|
||||
rc = dev->em28xx_write_regs(dev,
|
||||
gpio->reg,
|
||||
&gpio->val, 1);
|
||||
if (gpio->sleep > 0)
|
||||
msleep(gpio->sleep);
|
||||
|
||||
gpio++;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(em28xx_tuner_callback);
|
||||
|
@ -527,6 +510,10 @@ void em28xx_pre_card_setup(struct em28xx *dev)
|
|||
{
|
||||
int rc;
|
||||
|
||||
rc = em28xx_read_reg(dev, EM2880_R04_GPO);
|
||||
if (rc >= 0)
|
||||
dev->reg_gpo = rc;
|
||||
|
||||
dev->wait_after_write = 5;
|
||||
rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
|
||||
if (rc > 0) {
|
||||
|
@ -547,24 +534,24 @@ void em28xx_pre_card_setup(struct em28xx *dev)
|
|||
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
|
||||
case EM2880_BOARD_TERRATEC_HYBRID_XS:
|
||||
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
|
||||
em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
|
||||
em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
|
||||
em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
|
||||
em28xx_write_regs(dev, 0x08, "\xff", 1);
|
||||
em28xx_write_regs(dev, 0x04, "\x00", 1);
|
||||
msleep(100);
|
||||
em28xx_write_regs(dev, 0x04, "\x08", 1);
|
||||
msleep(100);
|
||||
em28xx_write_regs(dev, 0x08, "\xff", 1);
|
||||
msleep(50);
|
||||
em28xx_write_regs(dev, 0x08, "\x2d", 1);
|
||||
msleep(50);
|
||||
em28xx_write_regs(dev, 0x08, "\x3d", 1);
|
||||
|
||||
dev->analog_gpio = hauppauge_wintv_hvr_900_analog;
|
||||
dev->digital_gpio = hauppauge_wintv_hvr_900_digital;
|
||||
/* Sets GPO/GPIO sequences for this device */
|
||||
dev->analog_gpio = hauppauge_wintv_hvr_900_analog;
|
||||
dev->digital_gpio = hauppauge_wintv_hvr_900_digital;
|
||||
dev->tun_analog_gpio = hauppauge_wintv_hvr_900_tuner_callback;
|
||||
dev->tun_digital_gpio = hauppauge_wintv_hvr_900_tuner_callback;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
em28xx_gpio_set(dev, dev->tun_analog_gpio);
|
||||
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
|
||||
|
||||
/* Unlock device */
|
||||
em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
|
||||
}
|
||||
|
||||
void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
|
||||
|
|
|
@ -134,7 +134,10 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
|
|||
unsigned char *bufs;
|
||||
|
||||
if (dev->state & DEV_DISCONNECTED)
|
||||
return(-ENODEV);
|
||||
return -ENODEV;
|
||||
|
||||
if (len < 1)
|
||||
return -EINVAL;
|
||||
|
||||
bufs = kmalloc(len, GFP_KERNEL);
|
||||
|
||||
|
@ -162,7 +165,23 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
|
|||
|
||||
int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len)
|
||||
{
|
||||
return em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len);
|
||||
int rc;
|
||||
|
||||
rc = em28xx_write_regs_req(dev, USB_REQ_GET_STATUS, reg, buf, len);
|
||||
|
||||
/* Stores GPO/GPIO values at the cache, if changed
|
||||
Only write values should be stored, since input on a GPIO
|
||||
register will return the input bits.
|
||||
Not sure what happens on reading GPO register.
|
||||
*/
|
||||
if (rc >= 0) {
|
||||
if (reg == EM2880_R04_GPO)
|
||||
dev->reg_gpo = buf[0];
|
||||
else if (reg == EM28XX_R08_GPIO)
|
||||
dev->reg_gpio = buf[0];
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -176,12 +195,19 @@ static int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val,
|
|||
int oldval;
|
||||
u8 newval;
|
||||
|
||||
oldval = em28xx_read_reg(dev, reg);
|
||||
/* Uses cache for gpo/gpio registers */
|
||||
if (reg == EM2880_R04_GPO)
|
||||
oldval = dev->reg_gpo;
|
||||
else if (reg == EM28XX_R08_GPIO)
|
||||
oldval = dev->reg_gpio;
|
||||
else
|
||||
oldval = em28xx_read_reg(dev, reg);
|
||||
|
||||
if (oldval < 0)
|
||||
return oldval;
|
||||
|
||||
newval = (((u8) oldval) & ~bitmask) | (val & bitmask);
|
||||
|
||||
return em28xx_write_regs(dev, reg, &newval, 1);
|
||||
}
|
||||
|
||||
|
@ -472,6 +498,57 @@ int em28xx_set_alternate(struct em28xx *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!gpio)
|
||||
return rc;
|
||||
|
||||
dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
|
||||
if (dev->mode == EM28XX_ANALOG_MODE)
|
||||
dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x67", 1);
|
||||
else
|
||||
dev->em28xx_write_regs_req(dev, 0x00, 0x12, "\x37", 1);
|
||||
msleep(6);
|
||||
|
||||
/* Send GPIO reset sequences specified at board entry */
|
||||
while (gpio->sleep >= 0) {
|
||||
if (gpio->reg >= 0) {
|
||||
rc = em28xx_write_reg_bits(dev,
|
||||
gpio->reg,
|
||||
gpio->val,
|
||||
gpio->mask);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
if (gpio->sleep > 0)
|
||||
msleep(gpio->sleep);
|
||||
|
||||
gpio++;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode)
|
||||
{
|
||||
if (dev->mode == set_mode)
|
||||
return 0;
|
||||
|
||||
if (set_mode == EM28XX_MODE_UNDEFINED) {
|
||||
dev->mode = set_mode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev->mode = set_mode;
|
||||
|
||||
if (dev->mode == EM28XX_DIGITAL_MODE)
|
||||
return em28xx_gpio_set(dev, dev->digital_gpio);
|
||||
else
|
||||
return em28xx_gpio_set(dev, dev->analog_gpio);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(em28xx_set_mode);
|
||||
|
||||
/* ------------------------------------------------------------------
|
||||
URB control
|
||||
------------------------------------------------------------------*/
|
||||
|
@ -548,8 +625,7 @@ EXPORT_SYMBOL_GPL(em28xx_uninit_isoc);
|
|||
*/
|
||||
int em28xx_init_isoc(struct em28xx *dev, int max_packets,
|
||||
int num_bufs, int max_pkt_size,
|
||||
int (*isoc_copy) (struct em28xx *dev, struct urb *urb),
|
||||
int cap_type)
|
||||
int (*isoc_copy) (struct em28xx *dev, struct urb *urb))
|
||||
{
|
||||
struct em28xx_dmaqueue *dma_q = &dev->vidq;
|
||||
int i;
|
||||
|
@ -612,7 +688,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
|
|||
should also be using 'desc.bInterval'
|
||||
*/
|
||||
pipe = usb_rcvisocpipe(dev->udev,
|
||||
cap_type == EM28XX_ANALOG_CAPTURE ? 0x82 : 0x84);
|
||||
dev->mode == EM28XX_ANALOG_MODE ? 0x82 : 0x84);
|
||||
|
||||
usb_fill_int_urb(urb, dev->udev, pipe,
|
||||
dev->isoc_ctl.transfer_buffer[i], sb_size,
|
||||
|
@ -632,7 +708,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
|
|||
|
||||
init_waitqueue_head(&dma_q->wq);
|
||||
|
||||
em28xx_capture_start(dev, cap_type);
|
||||
em28xx_capture_start(dev, 1);
|
||||
|
||||
/* submit urbs and enables IRQ */
|
||||
for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
|
||||
|
|
|
@ -137,14 +137,17 @@ static inline int dvb_isoc_copy(struct em28xx *dev, struct urb *urb)
|
|||
|
||||
static int start_streaming(struct em28xx_dvb *dvb)
|
||||
{
|
||||
int rc;
|
||||
struct em28xx *dev = dvb->adapter.priv;
|
||||
|
||||
usb_set_interface(dev->udev, 0, 1);
|
||||
dev->em28xx_write_regs_req(dev, 0x00, 0x48, "\x00", 1);
|
||||
rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS,
|
||||
EM28XX_DVB_NUM_BUFS, EM28XX_DVB_MAX_PACKETSIZE,
|
||||
dvb_isoc_copy, EM28XX_DIGITAL_CAPTURE);
|
||||
dvb_isoc_copy);
|
||||
}
|
||||
|
||||
static int stop_streaming(struct em28xx_dvb *dvb)
|
||||
|
@ -152,6 +155,9 @@ static int stop_streaming(struct em28xx_dvb *dvb)
|
|||
struct em28xx *dev = dvb->adapter.priv;
|
||||
|
||||
em28xx_uninit_isoc(dev);
|
||||
|
||||
em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -368,13 +374,10 @@ static int dvb_init(struct em28xx *dev)
|
|||
}
|
||||
dev->dvb = dvb;
|
||||
|
||||
em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
|
||||
/* init frontend */
|
||||
switch (dev->model) {
|
||||
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
|
||||
/* Enable lgdt330x */
|
||||
dev->mode = EM28XX_DIGITAL_MODE;
|
||||
em28xx_tuner_callback(dev, XC2028_TUNER_RESET, 0);
|
||||
|
||||
dvb->frontend = dvb_attach(lgdt330x_attach,
|
||||
&em2880_lgdt3303_dev,
|
||||
&dev->i2c_adap);
|
||||
|
@ -384,9 +387,6 @@ static int dvb_init(struct em28xx *dev)
|
|||
}
|
||||
break;
|
||||
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
|
||||
/* Enable zl10353 */
|
||||
dev->mode = EM28XX_DIGITAL_MODE;
|
||||
em28xx_tuner_callback(dev, XC2028_TUNER_RESET, 0);
|
||||
dvb->frontend = dvb_attach(zl10353_attach,
|
||||
&em28xx_zl10353_with_xc3028,
|
||||
&dev->i2c_adap);
|
||||
|
@ -415,10 +415,12 @@ static int dvb_init(struct em28xx *dev)
|
|||
if (result < 0)
|
||||
goto out_free;
|
||||
|
||||
em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
|
||||
printk(KERN_INFO "Successfully loaded em28xx-dvb\n");
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
|
||||
kfree(dvb);
|
||||
dev->dvb = NULL;
|
||||
return result;
|
||||
|
|
|
@ -461,7 +461,7 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
|
|||
if (urb_init) {
|
||||
rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
|
||||
EM28XX_NUM_BUFS, dev->max_pkt_size,
|
||||
em28xx_isoc_copy, EM28XX_ANALOG_CAPTURE);
|
||||
em28xx_isoc_copy);
|
||||
if (rc < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
@ -1534,8 +1534,8 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
|
|||
em28xx_videodbg("open minor=%d type=%s users=%d\n",
|
||||
minor, v4l2_type_names[fh_type], dev->users);
|
||||
|
||||
fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
|
||||
|
||||
fh = kzalloc(sizeof(struct em28xx_fh), GFP_KERNEL);
|
||||
if (!fh) {
|
||||
em28xx_errdev("em28xx-video.c: Out of memory?!\n");
|
||||
return -ENOMEM;
|
||||
|
@ -1552,9 +1552,15 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
|
|||
dev->hscale = 0;
|
||||
dev->vscale = 0;
|
||||
|
||||
em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
|
||||
em28xx_set_alternate(dev);
|
||||
em28xx_resolution_set(dev);
|
||||
|
||||
/* Needed, since GPIO might have disabled power of
|
||||
some i2c device
|
||||
*/
|
||||
em28xx_config_i2c(dev);
|
||||
|
||||
}
|
||||
if (fh->radio) {
|
||||
em28xx_videodbg("video_open: setting radio device\n");
|
||||
|
@ -1568,6 +1574,7 @@ static int em28xx_v4l2_open(struct inode *inode, struct file *filp)
|
|||
sizeof(struct em28xx_buffer), fh);
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
return errCode;
|
||||
}
|
||||
|
||||
|
@ -1647,6 +1654,7 @@ static int em28xx_v4l2_close(struct inode *inode, struct file *filp)
|
|||
|
||||
/* do this before setting alternate! */
|
||||
em28xx_uninit_isoc(dev);
|
||||
em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
|
||||
|
||||
/* set alternate 0 */
|
||||
dev->alt = 0;
|
||||
|
|
|
@ -110,6 +110,7 @@
|
|||
#define EM2800_I2C_WRITE_TIMEOUT 20
|
||||
|
||||
enum em28xx_mode {
|
||||
EM28XX_MODE_UNDEFINED,
|
||||
EM28XX_ANALOG_MODE,
|
||||
EM28XX_DIGITAL_MODE,
|
||||
};
|
||||
|
@ -228,7 +229,7 @@ enum em28xx_decoder {
|
|||
|
||||
struct em28xx_reg_seq {
|
||||
int reg;
|
||||
unsigned char val;
|
||||
unsigned char val, mask;
|
||||
int sleep;
|
||||
};
|
||||
|
||||
|
@ -274,12 +275,6 @@ enum em28xx_dev_state {
|
|||
DEV_MISCONFIGURED = 0x04,
|
||||
};
|
||||
|
||||
enum em28xx_capture_mode {
|
||||
EM28XX_CAPTURE_OFF = 0,
|
||||
EM28XX_ANALOG_CAPTURE,
|
||||
EM28XX_DIGITAL_CAPTURE,
|
||||
};
|
||||
|
||||
#define EM28XX_AUDIO_BUFS 5
|
||||
#define EM28XX_NUM_AUDIO_PACKETS 64
|
||||
#define EM28XX_AUDIO_MAX_PACKET_SIZE 196 /* static value */
|
||||
|
@ -335,9 +330,12 @@ struct em28xx {
|
|||
/* Some older em28xx chips needs a waiting time after writing */
|
||||
unsigned int wait_after_write;
|
||||
|
||||
/* GPIO sequences for tuner callback */
|
||||
/* GPIO sequences for analog and digital mode */
|
||||
struct em28xx_reg_seq *analog_gpio, *digital_gpio;
|
||||
|
||||
/* GPIO sequences for tuner callbacks */
|
||||
struct em28xx_reg_seq *tun_analog_gpio, *tun_digital_gpio;
|
||||
|
||||
int video_inputs; /* number of video inputs */
|
||||
struct list_head devlist;
|
||||
|
||||
|
@ -415,6 +413,9 @@ struct em28xx {
|
|||
|
||||
enum em28xx_mode mode;
|
||||
|
||||
/* Caches GPO and GPIO registers */
|
||||
unsigned char reg_gpo, reg_gpio;
|
||||
|
||||
struct em28xx_dvb *dvb;
|
||||
};
|
||||
|
||||
|
@ -455,9 +456,10 @@ int em28xx_resolution_set(struct em28xx *dev);
|
|||
int em28xx_set_alternate(struct em28xx *dev);
|
||||
int em28xx_init_isoc(struct em28xx *dev, int max_packets,
|
||||
int num_bufs, int max_pkt_size,
|
||||
int (*isoc_copy) (struct em28xx *dev, struct urb *urb),
|
||||
int cap_type);
|
||||
int (*isoc_copy) (struct em28xx *dev, struct urb *urb));
|
||||
void em28xx_uninit_isoc(struct em28xx *dev);
|
||||
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
|
||||
int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
|
||||
|
||||
/* Provided by em28xx-video.c */
|
||||
int em28xx_register_extension(struct em28xx_ops *dev);
|
||||
|
|
Загрузка…
Ссылка в новой задаче