Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
config VIDEO_AU0828
tristate "Auvitek AU0828 support"
depends on I2C && INPUT && DVB_CORE && USB
select I2C_ALGOBIT
select VIDEO_TVEEPROM
select VIDEOBUF_VMALLOC
select DVB_AU8522_DTV if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_XC5000 if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_MXL5007T if MEDIA_SUBDRV_AUTOSELECT
select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT
---help---
This is a hybrid analog/digital tv capture driver for
Auvitek's AU0828 USB device.
To compile this driver as a module, choose M here: the
module will be called au0828
config VIDEO_AU0828_V4L2
bool "Auvitek AU0828 v4l2 analog video support"
depends on VIDEO_AU0828 && VIDEO_V4L2
select DVB_AU8522_V4L if MEDIA_SUBDRV_AUTOSELECT
default y
---help---
This is a video4linux driver for Auvitek's USB device.
Choose Y here to include support for v4l2 analog video
capture within the au0828 driver.

View File

@@ -0,0 +1,13 @@
au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o
ifeq ($(CONFIG_VIDEO_AU0828_V4L2),y)
au0828-objs += au0828-video.o au0828-vbi.o
endif
obj-$(CONFIG_VIDEO_AU0828) += au0828.o
ccflags-y += -Idrivers/media/tuners
ccflags-y += -Idrivers/media/dvb-core
ccflags-y += -Idrivers/media/dvb-frontends
ccflags-y += $(extra-cflags-y) $(extra-cflags-m)

View File

@@ -0,0 +1,353 @@
/*
* Driver for the Auvitek USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "au0828.h"
#include "au0828-cards.h"
#include "au8522.h"
#include "media/tuner.h"
#include "media/v4l2-common.h"
static void hvr950q_cs5340_audio(void *priv, int enable)
{
/* Because the HVR-950q shares an i2s bus between the cs5340 and the
au8522, we need to hold cs5340 in reset when using the au8522 */
struct au0828_dev *dev = priv;
if (enable == 1)
au0828_set(dev, REG_000, 0x10);
else
au0828_clear(dev, REG_000, 0x10);
}
struct au0828_board au0828_boards[] = {
[AU0828_BOARD_UNKNOWN] = {
.name = "Unknown board",
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
},
[AU0828_BOARD_HAUPPAUGE_HVR850] = {
.name = "Hauppauge HVR850",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
.i2c_clk_divider = AU0828_I2C_CLK_20KHZ,
.input = {
{
.type = AU0828_VMUX_TELEVISION,
.vmux = AU8522_COMPOSITE_CH4_SIF,
.amux = AU8522_AUDIO_SIF,
},
{
.type = AU0828_VMUX_COMPOSITE,
.vmux = AU8522_COMPOSITE_CH1,
.amux = AU8522_AUDIO_NONE,
.audio_setup = hvr950q_cs5340_audio,
},
{
.type = AU0828_VMUX_SVIDEO,
.vmux = AU8522_SVIDEO_CH13,
.amux = AU8522_AUDIO_NONE,
.audio_setup = hvr950q_cs5340_audio,
},
},
},
[AU0828_BOARD_HAUPPAUGE_HVR950Q] = {
.name = "Hauppauge HVR950Q",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
/* The au0828 hardware i2c implementation does not properly
support the xc5000's i2c clock stretching. So we need to
lower the clock frequency enough where the 15us clock
stretch fits inside of a normal clock cycle, or else the
au0828 fails to set the STOP bit. A 30 KHz clock puts the
clock pulse width at 18us */
.i2c_clk_divider = AU0828_I2C_CLK_20KHZ,
.input = {
{
.type = AU0828_VMUX_TELEVISION,
.vmux = AU8522_COMPOSITE_CH4_SIF,
.amux = AU8522_AUDIO_SIF,
},
{
.type = AU0828_VMUX_COMPOSITE,
.vmux = AU8522_COMPOSITE_CH1,
.amux = AU8522_AUDIO_NONE,
.audio_setup = hvr950q_cs5340_audio,
},
{
.type = AU0828_VMUX_SVIDEO,
.vmux = AU8522_SVIDEO_CH13,
.amux = AU8522_AUDIO_NONE,
.audio_setup = hvr950q_cs5340_audio,
},
},
},
[AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = {
.name = "Hauppauge HVR950Q rev xxF8",
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
},
[AU0828_BOARD_DVICO_FUSIONHDTV7] = {
.name = "DViCO FusionHDTV USB",
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
},
[AU0828_BOARD_HAUPPAUGE_WOODBURY] = {
.name = "Hauppauge Woodbury",
.tuner_type = UNSET,
.tuner_addr = ADDR_UNSET,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
},
};
/* Tuner callback function for au0828 boards. Currently only needed
* for HVR1500Q, which has an xc5000 tuner.
*/
int au0828_tuner_callback(void *priv, int component, int command, int arg)
{
struct au0828_dev *dev = priv;
dprintk(1, "%s()\n", __func__);
switch (dev->boardnr) {
case AU0828_BOARD_HAUPPAUGE_HVR850:
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
case AU0828_BOARD_DVICO_FUSIONHDTV7:
if (command == 0) {
/* Tuner Reset Command from xc5000 */
/* Drive the tuner into reset and out */
au0828_clear(dev, REG_001, 2);
mdelay(10);
au0828_set(dev, REG_001, 2);
mdelay(10);
return 0;
} else {
printk(KERN_ERR
"%s(): Unknown command.\n", __func__);
return -EINVAL;
}
break;
}
return 0; /* Should never be here */
}
static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data)
{
struct tveeprom tv;
tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data);
dev->board.tuner_type = tv.tuner_type;
/* Make sure we support the board model */
switch (tv.model) {
case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */
case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
case 72101: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
case 72201: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */
case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
case 72261: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */
case 72271: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */
case 72281: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */
case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */
case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */
break;
default:
printk(KERN_WARNING "%s: warning: "
"unknown hauppauge model #%d\n", __func__, tv.model);
break;
}
printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n",
__func__, tv.model);
}
void au0828_card_analog_fe_setup(struct au0828_dev *dev);
void au0828_card_setup(struct au0828_dev *dev)
{
static u8 eeprom[256];
dprintk(1, "%s()\n", __func__);
dev->board = au0828_boards[dev->boardnr];
if (dev->i2c_rc == 0) {
dev->i2c_client.addr = 0xa0 >> 1;
tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom));
}
switch (dev->boardnr) {
case AU0828_BOARD_HAUPPAUGE_HVR850:
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
case AU0828_BOARD_HAUPPAUGE_WOODBURY:
if (dev->i2c_rc == 0)
hauppauge_eeprom(dev, eeprom+0xa0);
break;
}
au0828_card_analog_fe_setup(dev);
}
void au0828_card_analog_fe_setup(struct au0828_dev *dev)
{
#ifdef CONFIG_VIDEO_AU0828_V4L2
struct tuner_setup tun_setup;
struct v4l2_subdev *sd;
unsigned int mode_mask = T_ANALOG_TV;
if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) {
/* Load the analog demodulator driver (note this would need to
be abstracted out if we ever need to support a different
demod) */
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"au8522", 0x8e >> 1, NULL);
if (sd == NULL)
printk(KERN_ERR "analog subdev registration failed\n");
}
/* Setup tuners */
if (dev->board.tuner_type != TUNER_ABSENT) {
/* Load the tuner module, which does the attach */
sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
"tuner", dev->board.tuner_addr, NULL);
if (sd == NULL)
printk(KERN_ERR "tuner subdev registration fail\n");
tun_setup.mode_mask = mode_mask;
tun_setup.type = dev->board.tuner_type;
tun_setup.addr = dev->board.tuner_addr;
tun_setup.tuner_callback = au0828_tuner_callback;
v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr,
&tun_setup);
}
#endif
}
/*
* The bridge has between 8 and 12 gpios.
* Regs 1 and 0 deal with output enables.
* Regs 3 and 2 deal with direction.
*/
void au0828_gpio_setup(struct au0828_dev *dev)
{
dprintk(1, "%s()\n", __func__);
switch (dev->boardnr) {
case AU0828_BOARD_HAUPPAUGE_HVR850:
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
case AU0828_BOARD_HAUPPAUGE_WOODBURY:
/* GPIO's
* 4 - CS5340
* 5 - AU8522 Demodulator
* 6 - eeprom W/P
* 7 - power supply
* 9 - XC5000 Tuner
*/
/* Into reset */
au0828_write(dev, REG_003, 0x02);
au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
au0828_write(dev, REG_001, 0x0);
au0828_write(dev, REG_000, 0x0);
msleep(100);
/* Out of reset (leave the cs5340 in reset until needed) */
au0828_write(dev, REG_003, 0x02);
au0828_write(dev, REG_001, 0x02);
au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
au0828_write(dev, REG_000, 0x80 | 0x40 | 0x20);
msleep(250);
break;
case AU0828_BOARD_DVICO_FUSIONHDTV7:
/* GPIO's
* 6 - ?
* 8 - AU8522 Demodulator
* 9 - XC5000 Tuner
*/
/* Into reset */
au0828_write(dev, REG_003, 0x02);
au0828_write(dev, REG_002, 0xa0);
au0828_write(dev, REG_001, 0x0);
au0828_write(dev, REG_000, 0x0);
msleep(100);
/* Out of reset */
au0828_write(dev, REG_003, 0x02);
au0828_write(dev, REG_002, 0xa0);
au0828_write(dev, REG_001, 0x02);
au0828_write(dev, REG_000, 0xa0);
msleep(250);
break;
}
}
/* table of devices that work with this driver */
struct usb_device_id au0828_usb_id_table[] = {
{ USB_DEVICE(0x2040, 0x7200),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7240),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR850 },
{ USB_DEVICE(0x0fe9, 0xd620),
.driver_info = AU0828_BOARD_DVICO_FUSIONHDTV7 },
{ USB_DEVICE(0x2040, 0x7210),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7217),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x721b),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x721e),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x721f),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7280),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x0fd9, 0x0008),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7201),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
{ USB_DEVICE(0x2040, 0x7211),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
{ USB_DEVICE(0x2040, 0x7281),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL },
{ USB_DEVICE(0x05e1, 0x0480),
.driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY },
{ USB_DEVICE(0x2040, 0x8200),
.driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY },
{ USB_DEVICE(0x2040, 0x7260),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7213),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ USB_DEVICE(0x2040, 0x7270),
.driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q },
{ },
};
MODULE_DEVICE_TABLE(usb, au0828_usb_id_table);

View File

@@ -0,0 +1,27 @@
/*
* Driver for the Auvitek USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define AU0828_BOARD_UNKNOWN 0
#define AU0828_BOARD_HAUPPAUGE_HVR950Q 1
#define AU0828_BOARD_HAUPPAUGE_HVR850 2
#define AU0828_BOARD_DVICO_FUSIONHDTV7 3
#define AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL 4
#define AU0828_BOARD_HAUPPAUGE_WOODBURY 5

View File

@@ -0,0 +1,318 @@
/*
* Driver for the Auvitek USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/mutex.h>
#include "au0828.h"
/*
* 1 = General debug messages
* 2 = USB handling
* 4 = I2C related
* 8 = Bridge related
*/
int au0828_debug;
module_param_named(debug, au0828_debug, int, 0644);
MODULE_PARM_DESC(debug, "enable debug messages");
static unsigned int disable_usb_speed_check;
module_param(disable_usb_speed_check, int, 0444);
MODULE_PARM_DESC(disable_usb_speed_check,
"override min bandwidth requirement of 480M bps");
#define _AU0828_BULKPIPE 0x03
#define _BULKPIPESIZE 0xffff
static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
u16 index);
static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
u16 index, unsigned char *cp, u16 size);
/* USB Direction */
#define CMD_REQUEST_IN 0x00
#define CMD_REQUEST_OUT 0x01
u32 au0828_readreg(struct au0828_dev *dev, u16 reg)
{
u8 result = 0;
recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, &result, 1);
dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, result);
return result;
}
u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val)
{
dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
return send_control_msg(dev, CMD_REQUEST_OUT, val, reg);
}
static int send_control_msg(struct au0828_dev *dev, u16 request, u32 value,
u16 index)
{
int status = -ENODEV;
if (dev->usbdev) {
/* cp must be memory that has been allocated by kmalloc */
status = usb_control_msg(dev->usbdev,
usb_sndctrlpipe(dev->usbdev, 0),
request,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_DEVICE,
value, index, NULL, 0, 1000);
status = min(status, 0);
if (status < 0) {
printk(KERN_ERR "%s() Failed sending control message, error %d.\n",
__func__, status);
}
}
return status;
}
static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
u16 index, unsigned char *cp, u16 size)
{
int status = -ENODEV;
mutex_lock(&dev->mutex);
if (dev->usbdev) {
status = usb_control_msg(dev->usbdev,
usb_rcvctrlpipe(dev->usbdev, 0),
request,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value, index,
dev->ctrlmsg, size, 1000);
status = min(status, 0);
if (status < 0) {
printk(KERN_ERR "%s() Failed receiving control message, error %d.\n",
__func__, status);
}
/* the host controller requires heap allocated memory, which
is why we didn't just pass "cp" into usb_control_msg */
memcpy(cp, dev->ctrlmsg, size);
}
mutex_unlock(&dev->mutex);
return status;
}
static void au0828_usb_release(struct au0828_dev *dev)
{
/* I2C */
au0828_i2c_unregister(dev);
kfree(dev);
}
#ifdef CONFIG_VIDEO_AU0828_V4L2
static void au0828_usb_v4l2_release(struct v4l2_device *v4l2_dev)
{
struct au0828_dev *dev =
container_of(v4l2_dev, struct au0828_dev, v4l2_dev);
v4l2_ctrl_handler_free(&dev->v4l2_ctrl_hdl);
v4l2_device_unregister(&dev->v4l2_dev);
au0828_usb_release(dev);
}
#endif
static void au0828_usb_disconnect(struct usb_interface *interface)
{
struct au0828_dev *dev = usb_get_intfdata(interface);
dprintk(1, "%s()\n", __func__);
/* Digital TV */
au0828_dvb_unregister(dev);
usb_set_intfdata(interface, NULL);
mutex_lock(&dev->mutex);
dev->usbdev = NULL;
mutex_unlock(&dev->mutex);
#ifdef CONFIG_VIDEO_AU0828_V4L2
if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) {
au0828_analog_unregister(dev);
v4l2_device_disconnect(&dev->v4l2_dev);
v4l2_device_put(&dev->v4l2_dev);
return;
}
#endif
au0828_usb_release(dev);
}
static int au0828_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
int ifnum;
#ifdef CONFIG_VIDEO_AU0828_V4L2
int retval;
#endif
struct au0828_dev *dev;
struct usb_device *usbdev = interface_to_usbdev(interface);
ifnum = interface->altsetting->desc.bInterfaceNumber;
if (ifnum != 0)
return -ENODEV;
dprintk(1, "%s() vendor id 0x%x device id 0x%x ifnum:%d\n", __func__,
le16_to_cpu(usbdev->descriptor.idVendor),
le16_to_cpu(usbdev->descriptor.idProduct),
ifnum);
/*
* Make sure we have 480 Mbps of bandwidth, otherwise things like
* video stream wouldn't likely work, since 12 Mbps is generally
* not enough even for most Digital TV streams.
*/
if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) {
printk(KERN_ERR "au0828: Device initialization failed.\n");
printk(KERN_ERR "au0828: Device must be connected to a "
"high-speed USB 2.0 port.\n");
return -ENODEV;
}
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
printk(KERN_ERR "%s() Unable to allocate memory\n", __func__);
return -ENOMEM;
}
mutex_init(&dev->lock);
mutex_lock(&dev->lock);
mutex_init(&dev->mutex);
mutex_init(&dev->dvb.lock);
dev->usbdev = usbdev;
dev->boardnr = id->driver_info;
#ifdef CONFIG_VIDEO_AU0828_V4L2
dev->v4l2_dev.release = au0828_usb_v4l2_release;
/* Create the v4l2_device */
retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev);
if (retval) {
pr_err("%s() v4l2_device_register failed\n",
__func__);
mutex_unlock(&dev->lock);
kfree(dev);
return retval;
}
/* This control handler will inherit the controls from au8522 */
retval = v4l2_ctrl_handler_init(&dev->v4l2_ctrl_hdl, 4);
if (retval) {
pr_err("%s() v4l2_ctrl_handler_init failed\n",
__func__);
mutex_unlock(&dev->lock);
kfree(dev);
return retval;
}
dev->v4l2_dev.ctrl_handler = &dev->v4l2_ctrl_hdl;
#endif
/* Power Up the bridge */
au0828_write(dev, REG_600, 1 << 4);
/* Bring up the GPIO's and supporting devices */
au0828_gpio_setup(dev);
/* I2C */
au0828_i2c_register(dev);
/* Setup */
au0828_card_setup(dev);
#ifdef CONFIG_VIDEO_AU0828_V4L2
/* Analog TV */
if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED)
au0828_analog_register(dev, interface);
#endif
/* Digital TV */
au0828_dvb_register(dev);
/* Store the pointer to the au0828_dev so it can be accessed in
au0828_usb_disconnect */
usb_set_intfdata(interface, dev);
printk(KERN_INFO "Registered device AU0828 [%s]\n",
dev->board.name == NULL ? "Unset" : dev->board.name);
mutex_unlock(&dev->lock);
return 0;
}
static struct usb_driver au0828_usb_driver = {
.name = DRIVER_NAME,
.probe = au0828_usb_probe,
.disconnect = au0828_usb_disconnect,
.id_table = au0828_usb_id_table,
};
static int __init au0828_init(void)
{
int ret;
if (au0828_debug & 1)
printk(KERN_INFO "%s() Debugging is enabled\n", __func__);
if (au0828_debug & 2)
printk(KERN_INFO "%s() USB Debugging is enabled\n", __func__);
if (au0828_debug & 4)
printk(KERN_INFO "%s() I2C Debugging is enabled\n", __func__);
if (au0828_debug & 8)
printk(KERN_INFO "%s() Bridge Debugging is enabled\n",
__func__);
printk(KERN_INFO "au0828 driver loaded\n");
ret = usb_register(&au0828_usb_driver);
if (ret)
printk(KERN_ERR "usb_register failed, error = %d\n", ret);
return ret;
}
static void __exit au0828_exit(void)
{
usb_deregister(&au0828_usb_driver);
}
module_init(au0828_init);
module_exit(au0828_exit);
MODULE_DESCRIPTION("Driver for Auvitek AU0828 based products");
MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
MODULE_LICENSE("GPL");
MODULE_VERSION("0.0.2");

View File

@@ -0,0 +1,499 @@
/*
* Driver for the Auvitek USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/suspend.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
#include "au0828.h"
#include "au8522.h"
#include "xc5000.h"
#include "mxl5007t.h"
#include "tda18271.h"
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define _AU0828_BULKPIPE 0x83
#define _BULKPIPESIZE 0xe522
static u8 hauppauge_hvr950q_led_states[] = {
0x00, /* off */
0x02, /* yellow */
0x04, /* green */
};
static struct au8522_led_config hauppauge_hvr950q_led_cfg = {
.gpio_output = 0x00e0,
.gpio_output_enable = 0x6006,
.gpio_output_disable = 0x0660,
.gpio_leds = 0x00e2,
.led_states = hauppauge_hvr950q_led_states,
.num_led_states = sizeof(hauppauge_hvr950q_led_states),
.vsb8_strong = 20 /* dB */ * 10,
.qam64_strong = 25 /* dB */ * 10,
.qam256_strong = 32 /* dB */ * 10,
};
static struct au8522_config hauppauge_hvr950q_config = {
.demod_address = 0x8e >> 1,
.status_mode = AU8522_DEMODLOCKING,
.qam_if = AU8522_IF_6MHZ,
.vsb_if = AU8522_IF_6MHZ,
.led_cfg = &hauppauge_hvr950q_led_cfg,
};
static struct au8522_config fusionhdtv7usb_config = {
.demod_address = 0x8e >> 1,
.status_mode = AU8522_DEMODLOCKING,
.qam_if = AU8522_IF_6MHZ,
.vsb_if = AU8522_IF_6MHZ,
};
static struct au8522_config hauppauge_woodbury_config = {
.demod_address = 0x8e >> 1,
.status_mode = AU8522_DEMODLOCKING,
.qam_if = AU8522_IF_4MHZ,
.vsb_if = AU8522_IF_3_25MHZ,
};
static struct xc5000_config hauppauge_xc5000a_config = {
.i2c_address = 0x61,
.if_khz = 6000,
.chip_id = XC5000A,
};
static struct xc5000_config hauppauge_xc5000c_config = {
.i2c_address = 0x61,
.if_khz = 6000,
.chip_id = XC5000C,
};
static struct mxl5007t_config mxl5007t_hvr950q_config = {
.xtal_freq_hz = MxL_XTAL_24_MHZ,
.if_freq_hz = MxL_IF_6_MHZ,
};
static struct tda18271_config hauppauge_woodbury_tunerconfig = {
.gate = TDA18271_GATE_DIGITAL,
};
static void au0828_restart_dvb_streaming(struct work_struct *work);
/*-------------------------------------------------------------------*/
static void urb_completion(struct urb *purb)
{
struct au0828_dev *dev = purb->context;
int ptype = usb_pipetype(purb->pipe);
unsigned char *ptr;
dprintk(2, "%s()\n", __func__);
if (!dev)
return;
if (dev->urb_streaming == 0)
return;
if (ptype != PIPE_BULK) {
printk(KERN_ERR "%s() Unsupported URB type %d\n",
__func__, ptype);
return;
}
/* See if the stream is corrupted (to work around a hardware
bug where the stream gets misaligned */
ptr = purb->transfer_buffer;
if (purb->actual_length > 0 && ptr[0] != 0x47) {
dprintk(1, "Need to restart streaming %02x len=%d!\n",
ptr[0], purb->actual_length);
schedule_work(&dev->restart_streaming);
return;
}
/* Feed the transport payload into the kernel demux */
dvb_dmx_swfilter_packets(&dev->dvb.demux,
purb->transfer_buffer, purb->actual_length / 188);
/* Clean the buffer before we requeue */
memset(purb->transfer_buffer, 0, URB_BUFSIZE);
/* Requeue URB */
usb_submit_urb(purb, GFP_ATOMIC);
}
static int stop_urb_transfer(struct au0828_dev *dev)
{
int i;
dprintk(2, "%s()\n", __func__);
dev->urb_streaming = 0;
for (i = 0; i < URB_COUNT; i++) {
usb_kill_urb(dev->urbs[i]);
kfree(dev->urbs[i]->transfer_buffer);
usb_free_urb(dev->urbs[i]);
}
return 0;
}
static int start_urb_transfer(struct au0828_dev *dev)
{
struct urb *purb;
int i, ret = -ENOMEM;
dprintk(2, "%s()\n", __func__);
if (dev->urb_streaming) {
dprintk(2, "%s: bulk xfer already running!\n", __func__);
return 0;
}
for (i = 0; i < URB_COUNT; i++) {
dev->urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urbs[i])
goto err;
purb = dev->urbs[i];
purb->transfer_buffer = kzalloc(URB_BUFSIZE, GFP_KERNEL);
if (!purb->transfer_buffer) {
usb_free_urb(purb);
dev->urbs[i] = NULL;
goto err;
}
purb->status = -EINPROGRESS;
usb_fill_bulk_urb(purb,
dev->usbdev,
usb_rcvbulkpipe(dev->usbdev,
_AU0828_BULKPIPE),
purb->transfer_buffer,
URB_BUFSIZE,
urb_completion,
dev);
}
for (i = 0; i < URB_COUNT; i++) {
ret = usb_submit_urb(dev->urbs[i], GFP_ATOMIC);
if (ret != 0) {
stop_urb_transfer(dev);
printk(KERN_ERR "%s: failed urb submission, "
"err = %d\n", __func__, ret);
return ret;
}
}
dev->urb_streaming = 1;
ret = 0;
err:
return ret;
}
static int au0828_dvb_start_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct au0828_dev *dev = (struct au0828_dev *) demux->priv;
struct au0828_dvb *dvb = &dev->dvb;
int ret = 0;
dprintk(1, "%s()\n", __func__);
if (!demux->dmx.frontend)
return -EINVAL;
if (dvb) {
mutex_lock(&dvb->lock);
if (dvb->feeding++ == 0) {
/* Start transport */
au0828_write(dev, 0x608, 0x90);
au0828_write(dev, 0x609, 0x72);
au0828_write(dev, 0x60a, 0x71);
au0828_write(dev, 0x60b, 0x01);
ret = start_urb_transfer(dev);
}
mutex_unlock(&dvb->lock);
}
return ret;
}
static int au0828_dvb_stop_feed(struct dvb_demux_feed *feed)
{
struct dvb_demux *demux = feed->demux;
struct au0828_dev *dev = (struct au0828_dev *) demux->priv;
struct au0828_dvb *dvb = &dev->dvb;
int ret = 0;
dprintk(1, "%s()\n", __func__);
if (dvb) {
mutex_lock(&dvb->lock);
if (--dvb->feeding == 0) {
/* Stop transport */
ret = stop_urb_transfer(dev);
au0828_write(dev, 0x60b, 0x00);
}
mutex_unlock(&dvb->lock);
}
return ret;
}
static void au0828_restart_dvb_streaming(struct work_struct *work)
{
struct au0828_dev *dev = container_of(work, struct au0828_dev,
restart_streaming);
struct au0828_dvb *dvb = &dev->dvb;
if (dev->urb_streaming == 0)
return;
dprintk(1, "Restarting streaming...!\n");
mutex_lock(&dvb->lock);
/* Stop transport */
stop_urb_transfer(dev);
au0828_write(dev, 0x608, 0x00);
au0828_write(dev, 0x609, 0x00);
au0828_write(dev, 0x60a, 0x00);
au0828_write(dev, 0x60b, 0x00);
/* Start transport */
au0828_write(dev, 0x608, 0x90);
au0828_write(dev, 0x609, 0x72);
au0828_write(dev, 0x60a, 0x71);
au0828_write(dev, 0x60b, 0x01);
start_urb_transfer(dev);
mutex_unlock(&dvb->lock);
}
static int dvb_register(struct au0828_dev *dev)
{
struct au0828_dvb *dvb = &dev->dvb;
int result;
dprintk(1, "%s()\n", __func__);
INIT_WORK(&dev->restart_streaming, au0828_restart_dvb_streaming);
/* register adapter */
result = dvb_register_adapter(&dvb->adapter, DRIVER_NAME, THIS_MODULE,
&dev->usbdev->dev, adapter_nr);
if (result < 0) {
printk(KERN_ERR "%s: dvb_register_adapter failed "
"(errno = %d)\n", DRIVER_NAME, result);
goto fail_adapter;
}
dvb->adapter.priv = dev;
/* register frontend */
result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
if (result < 0) {
printk(KERN_ERR "%s: dvb_register_frontend failed "
"(errno = %d)\n", DRIVER_NAME, result);
goto fail_frontend;
}
/* register demux stuff */
dvb->demux.dmx.capabilities =
DMX_TS_FILTERING | DMX_SECTION_FILTERING |
DMX_MEMORY_BASED_FILTERING;
dvb->demux.priv = dev;
dvb->demux.filternum = 256;
dvb->demux.feednum = 256;
dvb->demux.start_feed = au0828_dvb_start_feed;
dvb->demux.stop_feed = au0828_dvb_stop_feed;
result = dvb_dmx_init(&dvb->demux);
if (result < 0) {
printk(KERN_ERR "%s: dvb_dmx_init failed (errno = %d)\n",
DRIVER_NAME, result);
goto fail_dmx;
}
dvb->dmxdev.filternum = 256;
dvb->dmxdev.demux = &dvb->demux.dmx;
dvb->dmxdev.capabilities = 0;
result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
if (result < 0) {
printk(KERN_ERR "%s: dvb_dmxdev_init failed (errno = %d)\n",
DRIVER_NAME, result);
goto fail_dmxdev;
}
dvb->fe_hw.source = DMX_FRONTEND_0;
result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
printk(KERN_ERR "%s: add_frontend failed "
"(DMX_FRONTEND_0, errno = %d)\n", DRIVER_NAME, result);
goto fail_fe_hw;
}
dvb->fe_mem.source = DMX_MEMORY_FE;
result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
if (result < 0) {
printk(KERN_ERR "%s: add_frontend failed "
"(DMX_MEMORY_FE, errno = %d)\n", DRIVER_NAME, result);
goto fail_fe_mem;
}
result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
printk(KERN_ERR "%s: connect_frontend failed (errno = %d)\n",
DRIVER_NAME, result);
goto fail_fe_conn;
}
/* register network adapter */
dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
return 0;
fail_fe_conn:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
fail_fe_mem:
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
fail_fe_hw:
dvb_dmxdev_release(&dvb->dmxdev);
fail_dmxdev:
dvb_dmx_release(&dvb->demux);
fail_dmx:
dvb_unregister_frontend(dvb->frontend);
fail_frontend:
dvb_frontend_detach(dvb->frontend);
dvb_unregister_adapter(&dvb->adapter);
fail_adapter:
return result;
}
void au0828_dvb_unregister(struct au0828_dev *dev)
{
struct au0828_dvb *dvb = &dev->dvb;
dprintk(1, "%s()\n", __func__);
if (dvb->frontend == NULL)
return;
dvb_net_release(&dvb->net);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
dvb_dmxdev_release(&dvb->dmxdev);
dvb_dmx_release(&dvb->demux);
dvb_unregister_frontend(dvb->frontend);
dvb_frontend_detach(dvb->frontend);
dvb_unregister_adapter(&dvb->adapter);
}
/* All the DVB attach calls go here, this function get's modified
* for each new card. No other function in this file needs
* to change.
*/
int au0828_dvb_register(struct au0828_dev *dev)
{
struct au0828_dvb *dvb = &dev->dvb;
int ret;
dprintk(1, "%s()\n", __func__);
/* init frontend */
switch (dev->boardnr) {
case AU0828_BOARD_HAUPPAUGE_HVR850:
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
dvb->frontend = dvb_attach(au8522_attach,
&hauppauge_hvr950q_config,
&dev->i2c_adap);
if (dvb->frontend != NULL)
switch (dev->board.tuner_type) {
default:
case TUNER_XC5000:
dvb_attach(xc5000_attach, dvb->frontend,
&dev->i2c_adap,
&hauppauge_xc5000a_config);
break;
case TUNER_XC5000C:
dvb_attach(xc5000_attach, dvb->frontend,
&dev->i2c_adap,
&hauppauge_xc5000c_config);
break;
}
break;
case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
dvb->frontend = dvb_attach(au8522_attach,
&hauppauge_hvr950q_config,
&dev->i2c_adap);
if (dvb->frontend != NULL)
dvb_attach(mxl5007t_attach, dvb->frontend,
&dev->i2c_adap, 0x60,
&mxl5007t_hvr950q_config);
break;
case AU0828_BOARD_HAUPPAUGE_WOODBURY:
dvb->frontend = dvb_attach(au8522_attach,
&hauppauge_woodbury_config,
&dev->i2c_adap);
if (dvb->frontend != NULL)
dvb_attach(tda18271_attach, dvb->frontend,
0x60, &dev->i2c_adap,
&hauppauge_woodbury_tunerconfig);
break;
case AU0828_BOARD_DVICO_FUSIONHDTV7:
dvb->frontend = dvb_attach(au8522_attach,
&fusionhdtv7usb_config,
&dev->i2c_adap);
if (dvb->frontend != NULL) {
dvb_attach(xc5000_attach, dvb->frontend,
&dev->i2c_adap,
&hauppauge_xc5000a_config);
}
break;
default:
printk(KERN_WARNING "The frontend of your DVB/ATSC card "
"isn't supported yet\n");
break;
}
if (NULL == dvb->frontend) {
printk(KERN_ERR "%s() Frontend initialization failed\n",
__func__);
return -1;
}
/* define general-purpose callback pointer */
dvb->frontend->callback = au0828_tuner_callback;
/* register everything */
ret = dvb_register(dev);
if (ret < 0) {
if (dvb->frontend->ops.release)
dvb->frontend->ops.release(dvb->frontend);
return ret;
}
return 0;
}

View File

@@ -0,0 +1,402 @@
/*
* Driver for the Auvitek AU0828 USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/io.h>
#include "au0828.h"
#include "media/tuner.h"
#include <media/v4l2-common.h>
static int i2c_scan;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
#define I2C_WAIT_DELAY 25
#define I2C_WAIT_RETRY 1000
static inline int i2c_slave_did_write_ack(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_NO_WRITE_ACK ? 0 : 1;
}
static inline int i2c_slave_did_read_ack(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_NO_READ_ACK ? 0 : 1;
}
static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap)
{
int count;
for (count = 0; count < I2C_WAIT_RETRY; count++) {
if (!i2c_slave_did_read_ack(i2c_adap))
break;
udelay(I2C_WAIT_DELAY);
}
if (I2C_WAIT_RETRY == count)
return 0;
return 1;
}
static inline int i2c_is_read_busy(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_READ_DONE ? 0 : 1;
}
static int i2c_wait_read_done(struct i2c_adapter *i2c_adap)
{
int count;
for (count = 0; count < I2C_WAIT_RETRY; count++) {
if (!i2c_is_read_busy(i2c_adap))
break;
udelay(I2C_WAIT_DELAY);
}
if (I2C_WAIT_RETRY == count)
return 0;
return 1;
}
static inline int i2c_is_write_done(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_WRITE_DONE ? 1 : 0;
}
static int i2c_wait_write_done(struct i2c_adapter *i2c_adap)
{
int count;
for (count = 0; count < I2C_WAIT_RETRY; count++) {
if (i2c_is_write_done(i2c_adap))
break;
udelay(I2C_WAIT_DELAY);
}
if (I2C_WAIT_RETRY == count)
return 0;
return 1;
}
static inline int i2c_is_busy(struct i2c_adapter *i2c_adap)
{
struct au0828_dev *dev = i2c_adap->algo_data;
return au0828_read(dev, AU0828_I2C_STATUS_201) &
AU0828_I2C_STATUS_BUSY ? 1 : 0;
}
static int i2c_wait_done(struct i2c_adapter *i2c_adap)
{
int count;
for (count = 0; count < I2C_WAIT_RETRY; count++) {
if (!i2c_is_busy(i2c_adap))
break;
udelay(I2C_WAIT_DELAY);
}
if (I2C_WAIT_RETRY == count)
return 0;
return 1;
}
/* FIXME: Implement join handling correctly */
static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg, int joined_rlen)
{
int i, strobe = 0;
struct au0828_dev *dev = i2c_adap->algo_data;
dprintk(4, "%s()\n", __func__);
au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
/* Set the I2C clock */
if (((dev->board.tuner_type == TUNER_XC5000) ||
(dev->board.tuner_type == TUNER_XC5000C)) &&
(dev->board.tuner_addr == msg->addr) &&
(msg->len == 64)) {
/* Hack to speed up firmware load. The xc5000 lets us do up
to 400 KHz when in firmware download mode */
au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202,
AU0828_I2C_CLK_250KHZ);
} else {
/* Use the i2c clock speed in the board configuration */
au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202,
dev->board.i2c_clk_divider);
}
/* Hardware needs 8 bit addresses */
au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1);
dprintk(4, "SEND: %02x\n", msg->addr);
/* Deal with i2c_scan */
if (msg->len == 0) {
/* The analog tuner detection code makes use of the SMBUS_QUICK
message (which involves a zero length i2c write). To avoid
checking the status register when we didn't strobe out any
actual bytes to the bus, just do a read check. This is
consistent with how I saw i2c device checking done in the
USB trace of the Windows driver */
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_READ);
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (i2c_wait_read_ack(i2c_adap))
return -EIO;
return 0;
}
for (i = 0; i < msg->len;) {
dprintk(4, " %02x\n", msg->buf[i]);
au0828_write(dev, AU0828_I2C_WRITE_FIFO_205, msg->buf[i]);
strobe++;
i++;
if ((strobe >= 4) || (i >= msg->len)) {
/* Strobe the byte into the bus */
if (i < msg->len)
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_WRITE |
AU0828_I2C_TRIGGER_HOLD);
else
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_WRITE);
/* Reset strobe trigger */
strobe = 0;
if (!i2c_wait_write_done(i2c_adap))
return -EIO;
}
}
if (!i2c_wait_done(i2c_adap))
return -EIO;
dprintk(4, "\n");
return msg->len;
}
/* FIXME: Implement join handling correctly */
static int i2c_readbytes(struct i2c_adapter *i2c_adap,
const struct i2c_msg *msg, int joined)
{
struct au0828_dev *dev = i2c_adap->algo_data;
int i;
dprintk(4, "%s()\n", __func__);
au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
/* Set the I2C clock */
au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202,
dev->board.i2c_clk_divider);
/* Hardware needs 8 bit addresses */
au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1);
dprintk(4, " RECV:\n");
/* Deal with i2c_scan */
if (msg->len == 0) {
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_READ);
if (i2c_wait_read_ack(i2c_adap))
return -EIO;
return 0;
}
for (i = 0; i < msg->len;) {
i++;
if (i < msg->len)
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_READ |
AU0828_I2C_TRIGGER_HOLD);
else
au0828_write(dev, AU0828_I2C_TRIGGER_200,
AU0828_I2C_TRIGGER_READ);
if (!i2c_wait_read_done(i2c_adap))
return -EIO;
msg->buf[i-1] = au0828_read(dev, AU0828_I2C_READ_FIFO_209) &
0xff;
dprintk(4, " %02x\n", msg->buf[i-1]);
}
if (!i2c_wait_done(i2c_adap))
return -EIO;
dprintk(4, "\n");
return msg->len;
}
static int i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *msgs, int num)
{
int i, retval = 0;
dprintk(4, "%s(num = %d)\n", __func__, num);
for (i = 0; i < num; i++) {
dprintk(4, "%s(num = %d) addr = 0x%02x len = 0x%x\n",
__func__, num, msgs[i].addr, msgs[i].len);
if (msgs[i].flags & I2C_M_RD) {
/* read */
retval = i2c_readbytes(i2c_adap, &msgs[i], 0);
} else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
msgs[i].addr == msgs[i + 1].addr) {
/* write then read from same address */
retval = i2c_sendbytes(i2c_adap, &msgs[i],
msgs[i + 1].len);
if (retval < 0)
goto err;
i++;
retval = i2c_readbytes(i2c_adap, &msgs[i], 1);
} else {
/* write */
retval = i2c_sendbytes(i2c_adap, &msgs[i], 0);
}
if (retval < 0)
goto err;
}
return num;
err:
return retval;
}
static u32 au0828_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
}
static struct i2c_algorithm au0828_i2c_algo_template = {
.master_xfer = i2c_xfer,
.functionality = au0828_functionality,
};
/* ----------------------------------------------------------------------- */
static struct i2c_adapter au0828_i2c_adap_template = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.algo = &au0828_i2c_algo_template,
};
static struct i2c_client au0828_i2c_client_template = {
.name = "au0828 internal",
};
static char *i2c_devs[128] = {
[0x8e >> 1] = "au8522",
[0xa0 >> 1] = "eeprom",
[0xc2 >> 1] = "tuner/xc5000",
};
static void do_i2c_scan(char *name, struct i2c_client *c)
{
unsigned char buf;
int i, rc;
for (i = 0; i < 128; i++) {
c->addr = i;
rc = i2c_master_recv(c, &buf, 0);
if (rc < 0)
continue;
printk(KERN_INFO "%s: i2c scan: found device @ 0x%x [%s]\n",
name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
}
}
/* init + register i2c adapter */
int au0828_i2c_register(struct au0828_dev *dev)
{
dprintk(1, "%s()\n", __func__);
dev->i2c_adap = au0828_i2c_adap_template;
dev->i2c_algo = au0828_i2c_algo_template;
dev->i2c_client = au0828_i2c_client_template;
dev->i2c_adap.dev.parent = &dev->usbdev->dev;
strlcpy(dev->i2c_adap.name, DRIVER_NAME,
sizeof(dev->i2c_adap.name));
dev->i2c_adap.algo = &dev->i2c_algo;
dev->i2c_adap.algo_data = dev;
#ifdef CONFIG_VIDEO_AU0828_V4L2
i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
#else
i2c_set_adapdata(&dev->i2c_adap, dev);
#endif
i2c_add_adapter(&dev->i2c_adap);
dev->i2c_client.adapter = &dev->i2c_adap;
if (0 == dev->i2c_rc) {
printk(KERN_INFO "%s: i2c bus registered\n", DRIVER_NAME);
if (i2c_scan)
do_i2c_scan(DRIVER_NAME, &dev->i2c_client);
} else
printk(KERN_INFO "%s: i2c bus register FAILED\n", DRIVER_NAME);
return dev->i2c_rc;
}
int au0828_i2c_unregister(struct au0828_dev *dev)
{
i2c_del_adapter(&dev->i2c_adap);
return 0;
}

View File

@@ -0,0 +1,66 @@
/*
* Driver for the Auvitek USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* We'll start to rename these registers once we have a better
* understanding of their meaning.
*/
#define REG_000 0x000
#define REG_001 0x001
#define REG_002 0x002
#define REG_003 0x003
#define AU0828_SENSORCTRL_100 0x100
#define AU0828_SENSORCTRL_VBI_103 0x103
/* I2C registers */
#define AU0828_I2C_TRIGGER_200 0x200
#define AU0828_I2C_STATUS_201 0x201
#define AU0828_I2C_CLK_DIVIDER_202 0x202
#define AU0828_I2C_DEST_ADDR_203 0x203
#define AU0828_I2C_WRITE_FIFO_205 0x205
#define AU0828_I2C_READ_FIFO_209 0x209
#define AU0828_I2C_MULTIBYTE_MODE_2FF 0x2ff
/* Audio registers */
#define AU0828_AUDIOCTRL_50C 0x50C
#define REG_600 0x600
/*********************************************************************/
/* Here are constants for values associated with the above registers */
/* I2C Trigger (Reg 0x200) */
#define AU0828_I2C_TRIGGER_WRITE 0x01
#define AU0828_I2C_TRIGGER_READ 0x20
#define AU0828_I2C_TRIGGER_HOLD 0x40
/* I2C Status (Reg 0x201) */
#define AU0828_I2C_STATUS_READ_DONE 0x01
#define AU0828_I2C_STATUS_NO_READ_ACK 0x02
#define AU0828_I2C_STATUS_WRITE_DONE 0x04
#define AU0828_I2C_STATUS_NO_WRITE_ACK 0x08
#define AU0828_I2C_STATUS_BUSY 0x10
/* I2C Clock Divider (Reg 0x202) */
#define AU0828_I2C_CLK_250KHZ 0x07
#define AU0828_I2C_CLK_100KHZ 0x14
#define AU0828_I2C_CLK_30KHZ 0x40
#define AU0828_I2C_CLK_20KHZ 0x60

View File

@@ -0,0 +1,138 @@
/*
au0828-vbi.c - VBI driver for au0828
Copyright (C) 2010 Devin Heitmueller <dheitmueller@kernellabs.com>
This work was sponsored by GetWellNetwork Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include "au0828.h"
static unsigned int vbibufs = 5;
module_param(vbibufs, int, 0644);
MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
/* ------------------------------------------------------------------ */
static void
free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf)
{
struct au0828_fh *fh = vq->priv_data;
struct au0828_dev *dev = fh->dev;
unsigned long flags = 0;
if (in_interrupt())
BUG();
/* We used to wait for the buffer to finish here, but this didn't work
because, as we were keeping the state as VIDEOBUF_QUEUED,
videobuf_queue_cancel marked it as finished for us.
(Also, it could wedge forever if the hardware was misconfigured.)
This should be safe; by the time we get here, the buffer isn't
queued anymore. If we ever start marking the buffers as
VIDEOBUF_ACTIVE, it won't be, though.
*/
spin_lock_irqsave(&dev->slock, flags);
if (dev->isoc_ctl.vbi_buf == buf)
dev->isoc_ctl.vbi_buf = NULL;
spin_unlock_irqrestore(&dev->slock, flags);
videobuf_vmalloc_free(&buf->vb);
buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
static int
vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
{
struct au0828_fh *fh = q->priv_data;
struct au0828_dev *dev = fh->dev;
*size = dev->vbi_width * dev->vbi_height * 2;
if (0 == *count)
*count = vbibufs;
if (*count < 2)
*count = 2;
if (*count > 32)
*count = 32;
return 0;
}
static int
vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
enum v4l2_field field)
{
struct au0828_fh *fh = q->priv_data;
struct au0828_dev *dev = fh->dev;
struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
int rc = 0;
buf->vb.size = dev->vbi_width * dev->vbi_height * 2;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
buf->vb.width = dev->vbi_width;
buf->vb.height = dev->vbi_height;
buf->vb.field = field;
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
rc = videobuf_iolock(q, &buf->vb, NULL);
if (rc < 0)
goto fail;
}
buf->vb.state = VIDEOBUF_PREPARED;
return 0;
fail:
free_buffer(q, buf);
return rc;
}
static void
vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
struct au0828_buffer *buf = container_of(vb,
struct au0828_buffer,
vb);
struct au0828_fh *fh = vq->priv_data;
struct au0828_dev *dev = fh->dev;
struct au0828_dmaqueue *vbiq = &dev->vbiq;
buf->vb.state = VIDEOBUF_QUEUED;
list_add_tail(&buf->vb.queue, &vbiq->active);
}
static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
free_buffer(q, buf);
}
struct videobuf_queue_ops au0828_vbi_qops = {
.buf_setup = vbi_setup,
.buf_prepare = vbi_prepare,
.buf_queue = vbi_queue,
.buf_release = vbi_release,
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,313 @@
/*
* Driver for the Auvitek AU0828 USB bridge
*
* Copyright (c) 2008 Steven Toth <stoth@linuxtv.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/usb.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <media/tveeprom.h>
/* Analog */
#include <linux/videodev2.h>
#include <media/videobuf-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
/* DVB */
#include "demux.h"
#include "dmxdev.h"
#include "dvb_demux.h"
#include "dvb_frontend.h"
#include "dvb_net.h"
#include "dvbdev.h"
#include "au0828-reg.h"
#include "au0828-cards.h"
#define DRIVER_NAME "au0828"
#define URB_COUNT 16
#define URB_BUFSIZE (0xe522)
/* Analog constants */
#define NTSC_STD_W 720
#define NTSC_STD_H 480
#define AU0828_INTERLACED_DEFAULT 1
#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0)
/* Defination for AU0828 USB transfer */
#define AU0828_MAX_ISO_BUFS 12 /* maybe resize this value in the future */
#define AU0828_ISO_PACKETS_PER_URB 128
#define AU0828_MIN_BUF 4
#define AU0828_DEF_BUF 8
#define AU0828_MAX_INPUT 4
/* au0828 resource types (used for res_get/res_lock etc */
#define AU0828_RESOURCE_VIDEO 0x01
#define AU0828_RESOURCE_VBI 0x02
enum au0828_itype {
AU0828_VMUX_UNDEFINED = 0,
AU0828_VMUX_COMPOSITE,
AU0828_VMUX_SVIDEO,
AU0828_VMUX_CABLE,
AU0828_VMUX_TELEVISION,
AU0828_VMUX_DVB,
AU0828_VMUX_DEBUG
};
struct au0828_input {
enum au0828_itype type;
unsigned int vmux;
unsigned int amux;
void (*audio_setup) (void *priv, int enable);
};
struct au0828_board {
char *name;
unsigned int tuner_type;
unsigned char tuner_addr;
unsigned char i2c_clk_divider;
struct au0828_input input[AU0828_MAX_INPUT];
};
struct au0828_dvb {
struct mutex lock;
struct dvb_adapter adapter;
struct dvb_frontend *frontend;
struct dvb_demux demux;
struct dmxdev dmxdev;
struct dmx_frontend fe_hw;
struct dmx_frontend fe_mem;
struct dvb_net net;
int feeding;
};
enum au0828_stream_state {
STREAM_OFF,
STREAM_INTERRUPT,
STREAM_ON
};
#define AUVI_INPUT(nr) (dev->board.input[nr])
/* device state */
enum au0828_dev_state {
DEV_INITIALIZED = 0x01,
DEV_DISCONNECTED = 0x02,
DEV_MISCONFIGURED = 0x04
};
struct au0828_fh {
/* must be the first field of this struct! */
struct v4l2_fh fh;
struct au0828_dev *dev;
unsigned int resources;
struct videobuf_queue vb_vidq;
struct videobuf_queue vb_vbiq;
enum v4l2_buf_type type;
};
struct au0828_usb_isoc_ctl {
/* max packet size of isoc transaction */
int max_pkt_size;
/* number of allocated urbs */
int num_bufs;
/* urb for isoc transfers */
struct urb **urb;
/* transfer buffers for isoc transfer */
char **transfer_buffer;
/* Last buffer command and region */
u8 cmd;
int pos, size, pktsize;
/* Last field: ODD or EVEN? */
int field;
/* Stores incomplete commands */
u32 tmp_buf;
int tmp_buf_len;
/* Stores already requested buffers */
struct au0828_buffer *buf;
struct au0828_buffer *vbi_buf;
/* Stores the number of received fields */
int nfields;
/* isoc urb callback */
int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb);
};
/* buffer for one video frame */
struct au0828_buffer {
/* common v4l buffer stuff -- must be first */
struct videobuf_buffer vb;
struct list_head frame;
int top_field;
int receiving;
};
struct au0828_dmaqueue {
struct list_head active;
struct list_head queued;
wait_queue_head_t wq;
/* Counters to control buffer fill */
int pos;
};
struct au0828_dev {
struct mutex mutex;
struct usb_device *usbdev;
int boardnr;
struct au0828_board board;
u8 ctrlmsg[64];
/* I2C */
struct i2c_adapter i2c_adap;
struct i2c_algorithm i2c_algo;
struct i2c_client i2c_client;
u32 i2c_rc;
/* Digital */
struct au0828_dvb dvb;
struct work_struct restart_streaming;
#ifdef CONFIG_VIDEO_AU0828_V4L2
/* Analog */
struct v4l2_device v4l2_dev;
struct v4l2_ctrl_handler v4l2_ctrl_hdl;
#endif
int users;
unsigned int resources; /* resources in use */
struct video_device *vdev;
struct video_device *vbi_dev;
struct timer_list vid_timeout;
int vid_timeout_running;
struct timer_list vbi_timeout;
int vbi_timeout_running;
int width;
int height;
int vbi_width;
int vbi_height;
u32 vbi_read;
v4l2_std_id std;
u32 field_size;
u32 frame_size;
u32 bytesperline;
int type;
u8 ctrl_ainput;
__u8 isoc_in_endpointaddr;
u8 isoc_init_ok;
int greenscreen_detected;
unsigned int frame_count;
int ctrl_freq;
int input_type;
int std_set_in_tuner_core;
unsigned int ctrl_input;
enum au0828_dev_state dev_state;
enum au0828_stream_state stream_state;
wait_queue_head_t open;
struct mutex lock;
/* Isoc control struct */
struct au0828_dmaqueue vidq;
struct au0828_dmaqueue vbiq;
struct au0828_usb_isoc_ctl isoc_ctl;
spinlock_t slock;
/* usb transfer */
int alt; /* alternate */
int max_pkt_size; /* max packet size of isoc transaction */
int num_alt; /* Number of alternative settings */
unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
struct urb *urb[AU0828_MAX_ISO_BUFS]; /* urb for isoc transfers */
char *transfer_buffer[AU0828_MAX_ISO_BUFS];/* transfer buffers for isoc
transfer */
/* USB / URB Related */
int urb_streaming;
struct urb *urbs[URB_COUNT];
};
/* ----------------------------------------------------------- */
#define au0828_read(dev, reg) au0828_readreg(dev, reg)
#define au0828_write(dev, reg, value) au0828_writereg(dev, reg, value)
#define au0828_andor(dev, reg, mask, value) \
au0828_writereg(dev, reg, \
(au0828_readreg(dev, reg) & ~(mask)) | ((value) & (mask)))
#define au0828_set(dev, reg, bit) au0828_andor(dev, (reg), (bit), (bit))
#define au0828_clear(dev, reg, bit) au0828_andor(dev, (reg), (bit), 0)
/* ----------------------------------------------------------- */
/* au0828-core.c */
extern u32 au0828_read(struct au0828_dev *dev, u16 reg);
extern u32 au0828_write(struct au0828_dev *dev, u16 reg, u32 val);
extern int au0828_debug;
/* ----------------------------------------------------------- */
/* au0828-cards.c */
extern struct au0828_board au0828_boards[];
extern struct usb_device_id au0828_usb_id_table[];
extern void au0828_gpio_setup(struct au0828_dev *dev);
extern int au0828_tuner_callback(void *priv, int component,
int command, int arg);
extern void au0828_card_setup(struct au0828_dev *dev);
/* ----------------------------------------------------------- */
/* au0828-i2c.c */
extern int au0828_i2c_register(struct au0828_dev *dev);
extern int au0828_i2c_unregister(struct au0828_dev *dev);
/* ----------------------------------------------------------- */
/* au0828-video.c */
int au0828_analog_register(struct au0828_dev *dev,
struct usb_interface *interface);
int au0828_analog_stream_disable(struct au0828_dev *d);
void au0828_analog_unregister(struct au0828_dev *dev);
/* ----------------------------------------------------------- */
/* au0828-dvb.c */
extern int au0828_dvb_register(struct au0828_dev *dev);
extern void au0828_dvb_unregister(struct au0828_dev *dev);
/* au0828-vbi.c */
extern struct videobuf_queue_ops au0828_vbi_qops;
#define dprintk(level, fmt, arg...)\
do { if (au0828_debug & level)\
printk(KERN_DEBUG DRIVER_NAME "/0: " fmt, ## arg);\
} while (0)