Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,36 @@
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
select VIDEO_TUNER
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.
config VIDEO_AU0828_RC
bool "AU0828 Remote Controller support"
depends on RC_CORE
depends on VIDEO_AU0828
---help---
Enables Remote Controller support on au0828 driver.

View file

@ -0,0 +1,17 @@
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
ifeq ($(CONFIG_VIDEO_AU0828_RC),y)
au0828-objs += au0828-input.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,362 @@
/*
* 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);
}
/*
* WARNING: There's a quirks table at sound/usb/quirks-table.h
* that should also be updated every time a new device with V4L2 support
* is added here.
*/
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,
.has_ir_i2c = 1,
.has_analog = 1,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
.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,
.has_ir_i2c = 1,
.has_analog = 1,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
.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 = TUNER_XC5000,
.tuner_addr = 0x61,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
},
[AU0828_BOARD_DVICO_FUSIONHDTV7] = {
.name = "DViCO FusionHDTV USB",
.tuner_type = TUNER_XC5000,
.tuner_addr = 0x61,
.i2c_clk_divider = AU0828_I2C_CLK_250KHZ,
},
[AU0828_BOARD_HAUPPAUGE_WOODBURY] = {
.name = "Hauppauge Woodbury",
.tuner_type = TUNER_NXP_TDA18271,
.tuner_addr = 0x60,
.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 {
pr_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:
pr_warn("%s: warning: unknown hauppauge model #%d\n",
__func__, tv.model);
break;
}
pr_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)
pr_err("analog subdev registration failed\n");
}
/* Setup tuners */
if (dev->board.tuner_type != TUNER_ABSENT && dev->board.has_analog) {
/* 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)
pr_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
*/
/* Set relevant GPIOs as outputs (leave the EEPROM W/P
as an input since we will never touch it and it has
a pullup) */
au0828_write(dev, REG_003, 0x02);
au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
/* Into reset */
au0828_write(dev, REG_001, 0x0);
au0828_write(dev, REG_000, 0x0);
msleep(50);
/* Bring power supply out of reset */
au0828_write(dev, REG_000, 0x80);
msleep(50);
/* Bring xc5000 and au8522 out of reset (leave the
cs5340 in reset until needed) */
au0828_write(dev, REG_001, 0x02); /* xc5000 */
au0828_write(dev, REG_000, 0x80 | 0x20); /* PS + au8522 */
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,376 @@
/*
* 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 <linux/module.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/mutex.h>
/*
* 1 = General debug messages
* 2 = USB handling
* 4 = I2C related
* 8 = Bridge related
* 16 = IR related
*/
int au0828_debug;
module_param_named(debug, au0828_debug, int, 0644);
MODULE_PARM_DESC(debug,
"set debug bitmask: 1=general, 2=USB, 4=I2C, 8=bridge, 16=IR");
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) {
pr_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) {
pr_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__);
au0828_rc_unregister(dev);
/* 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;
int retval = 0;
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) {
pr_err("au0828: Device initialization failed.\n");
pr_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) {
pr_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 */
retval = au0828_dvb_register(dev);
if (retval)
pr_err("%s() au0282_dev_register failed\n",
__func__);
/* Remote controller */
au0828_rc_register(dev);
/*
* Store the pointer to the au0828_dev so it can be accessed in
* au0828_usb_disconnect
*/
usb_set_intfdata(interface, dev);
pr_info("Registered device AU0828 [%s]\n",
dev->board.name == NULL ? "Unset" : dev->board.name);
mutex_unlock(&dev->lock);
return retval;
}
static int au0828_suspend(struct usb_interface *interface,
pm_message_t message)
{
struct au0828_dev *dev = usb_get_intfdata(interface);
if (!dev)
return 0;
pr_info("Suspend\n");
au0828_rc_suspend(dev);
au0828_v4l2_suspend(dev);
au0828_dvb_suspend(dev);
/* FIXME: should suspend also ATV/DTV */
return 0;
}
static int au0828_resume(struct usb_interface *interface)
{
struct au0828_dev *dev = usb_get_intfdata(interface);
if (!dev)
return 0;
pr_info("Resume\n");
/* Power Up the bridge */
au0828_write(dev, REG_600, 1 << 4);
/* Bring up the GPIO's and supporting devices */
au0828_gpio_setup(dev);
au0828_rc_resume(dev);
au0828_v4l2_resume(dev);
au0828_dvb_resume(dev);
/* FIXME: should resume also ATV/DTV */
return 0;
}
static struct usb_driver au0828_usb_driver = {
.name = KBUILD_MODNAME,
.probe = au0828_usb_probe,
.disconnect = au0828_usb_disconnect,
.id_table = au0828_usb_id_table,
.suspend = au0828_suspend,
.resume = au0828_resume,
.reset_resume = au0828_resume,
};
static int __init au0828_init(void)
{
int ret;
if (au0828_debug & 1)
pr_info("%s() Debugging is enabled\n", __func__);
if (au0828_debug & 2)
pr_info("%s() USB Debugging is enabled\n", __func__);
if (au0828_debug & 4)
pr_info("%s() I2C Debugging is enabled\n", __func__);
if (au0828_debug & 8)
pr_info("%s() Bridge Debugging is enabled\n",
__func__);
if (au0828_debug & 16)
pr_info("%s() IR Debugging is enabled\n",
__func__);
pr_info("au0828 driver loaded\n");
ret = usb_register(&au0828_usb_driver);
if (ret)
pr_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.3");

View file

@ -0,0 +1,658 @@
/*
* 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 <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
#include "au8522.h"
#include "xc5000.h"
#include "mxl5007t.h"
#include "tda18271.h"
static int preallocate_big_buffers;
module_param_named(preallocate_big_buffers, preallocate_big_buffers, int, 0644);
MODULE_PARM_DESC(preallocate_big_buffers, "Preallocate the larger transfer buffers at module load time");
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: %d\n", __func__, purb->actual_length);
if (!dev) {
dprintk(2, "%s: no dev!\n", __func__);
return;
}
if (!dev->urb_streaming) {
dprintk(2, "%s: not streaming!\n", __func__);
return;
}
if (ptype != PIPE_BULK) {
pr_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__);
if (!dev->urb_streaming)
return 0;
dev->urb_streaming = false;
for (i = 0; i < URB_COUNT; i++) {
if (dev->urbs[i]) {
usb_kill_urb(dev->urbs[i]);
if (!preallocate_big_buffers)
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];
if (preallocate_big_buffers)
purb->transfer_buffer = dev->dig_transfer_buffer[i];
else
purb->transfer_buffer = kzalloc(URB_BUFSIZE,
GFP_KERNEL);
if (!purb->transfer_buffer) {
usb_free_urb(purb);
dev->urbs[i] = NULL;
pr_err("%s: failed big buffer allocation, err = %d\n",
__func__, ret);
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);
pr_err("%s: failed urb submission, err = %d\n",
__func__, ret);
return ret;
}
}
dev->urb_streaming = true;
ret = 0;
err:
return ret;
}
static void au0828_start_transport(struct au0828_dev *dev)
{
au0828_write(dev, 0x608, 0x90);
au0828_write(dev, 0x609, 0x72);
au0828_write(dev, 0x60a, 0x71);
au0828_write(dev, 0x60b, 0x01);
}
static void au0828_stop_transport(struct au0828_dev *dev, int full_stop)
{
if (full_stop) {
au0828_write(dev, 0x608, 0x00);
au0828_write(dev, 0x609, 0x00);
au0828_write(dev, 0x60a, 0x00);
}
au0828_write(dev, 0x60b, 0x00);
}
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->frontend) {
mutex_lock(&dvb->lock);
dvb->start_count++;
dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__,
dvb->start_count, dvb->stop_count);
if (dvb->feeding++ == 0) {
/* Start transport */
au0828_start_transport(dev);
ret = start_urb_transfer(dev);
if (ret < 0) {
au0828_stop_transport(dev, 0);
dvb->feeding--; /* We ran out of memory... */
}
}
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->frontend) {
cancel_work_sync(&dev->restart_streaming);
mutex_lock(&dvb->lock);
dvb->stop_count++;
dprintk(1, "%s(), start_count: %d, stop_count: %d\n", __func__,
dvb->start_count, dvb->stop_count);
if (dvb->feeding > 0) {
dvb->feeding--;
if (dvb->feeding == 0) {
/* Stop transport */
ret = stop_urb_transfer(dev);
au0828_stop_transport(dev, 0);
}
}
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)
return;
dprintk(1, "Restarting streaming...!\n");
mutex_lock(&dvb->lock);
/* Stop transport */
stop_urb_transfer(dev);
au0828_stop_transport(dev, 1);
/* Start transport */
au0828_start_transport(dev);
start_urb_transfer(dev);
mutex_unlock(&dvb->lock);
}
static int au0828_set_frontend(struct dvb_frontend *fe)
{
struct au0828_dev *dev = fe->dvb->priv;
struct au0828_dvb *dvb = &dev->dvb;
int ret, was_streaming;
mutex_lock(&dvb->lock);
was_streaming = dev->urb_streaming;
if (was_streaming) {
au0828_stop_transport(dev, 1);
/*
* We can't hold a mutex here, as the restart_streaming
* kthread may also hold it.
*/
mutex_unlock(&dvb->lock);
cancel_work_sync(&dev->restart_streaming);
mutex_lock(&dvb->lock);
stop_urb_transfer(dev);
}
mutex_unlock(&dvb->lock);
ret = dvb->set_frontend(fe);
if (was_streaming) {
mutex_lock(&dvb->lock);
au0828_start_transport(dev);
start_urb_transfer(dev);
mutex_unlock(&dvb->lock);
}
return ret;
}
static int dvb_register(struct au0828_dev *dev)
{
struct au0828_dvb *dvb = &dev->dvb;
int result;
dprintk(1, "%s()\n", __func__);
if (preallocate_big_buffers) {
int i;
for (i = 0; i < URB_COUNT; i++) {
dev->dig_transfer_buffer[i] = kzalloc(URB_BUFSIZE,
GFP_KERNEL);
if (!dev->dig_transfer_buffer[i]) {
result = -ENOMEM;
pr_err("failed buffer allocation (errno = %d)\n",
result);
goto fail_adapter;
}
}
}
INIT_WORK(&dev->restart_streaming, au0828_restart_dvb_streaming);
/* register adapter */
result = dvb_register_adapter(&dvb->adapter,
KBUILD_MODNAME, THIS_MODULE,
&dev->usbdev->dev, adapter_nr);
if (result < 0) {
pr_err("dvb_register_adapter failed (errno = %d)\n",
result);
goto fail_adapter;
}
dvb->adapter.priv = dev;
/* register frontend */
result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
if (result < 0) {
pr_err("dvb_register_frontend failed (errno = %d)\n",
result);
goto fail_frontend;
}
/* Hook dvb frontend */
dvb->set_frontend = dvb->frontend->ops.set_frontend;
dvb->frontend->ops.set_frontend = au0828_set_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) {
pr_err("dvb_dmx_init failed (errno = %d)\n", 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) {
pr_err("dvb_dmxdev_init failed (errno = %d)\n", 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) {
pr_err("add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
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) {
pr_err("add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
result);
goto fail_fe_mem;
}
result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
if (result < 0) {
pr_err("connect_frontend failed (errno = %d)\n", result);
goto fail_fe_conn;
}
/* register network adapter */
dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
dvb->start_count = 0;
dvb->stop_count = 0;
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:
if (preallocate_big_buffers) {
int i;
for (i = 0; i < URB_COUNT; i++)
kfree(dev->dig_transfer_buffer[i]);
}
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;
cancel_work_sync(&dev->restart_streaming);
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);
if (preallocate_big_buffers) {
int i;
for (i = 0; i < URB_COUNT; i++)
kfree(dev->dig_transfer_buffer[i]);
}
dvb->frontend = NULL;
}
/* 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:
pr_warn("The frontend of your DVB/ATSC card isn't supported yet\n");
break;
}
if (NULL == dvb->frontend) {
pr_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);
dvb->frontend = NULL;
return ret;
}
return 0;
}
void au0828_dvb_suspend(struct au0828_dev *dev)
{
struct au0828_dvb *dvb = &dev->dvb;
int rc;
if (dvb->frontend) {
if (dev->urb_streaming) {
cancel_work_sync(&dev->restart_streaming);
/* Stop transport */
mutex_lock(&dvb->lock);
stop_urb_transfer(dev);
au0828_stop_transport(dev, 1);
mutex_unlock(&dvb->lock);
dev->need_urb_start = true;
}
/* suspend frontend - does tuner and fe to sleep */
rc = dvb_frontend_suspend(dvb->frontend);
pr_info("au0828_dvb_suspend(): Suspending DVB fe %d\n", rc);
}
}
void au0828_dvb_resume(struct au0828_dev *dev)
{
struct au0828_dvb *dvb = &dev->dvb;
int rc;
if (dvb->frontend) {
/* resume frontend - does fe and tuner init */
rc = dvb_frontend_resume(dvb->frontend);
pr_info("au0828_dvb_resume(): Resuming DVB fe %d\n", rc);
if (dev->need_urb_start) {
/* Start transport */
mutex_lock(&dvb->lock);
au0828_start_transport(dev);
start_urb_transfer(dev);
mutex_unlock(&dvb->lock);
}
}
}

View file

@ -0,0 +1,414 @@
/*
* 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 "au0828.h"
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/io.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;
u8 i2c_speed = dev->board.i2c_clk_divider;
dprintk(4, "%s()\n", __func__);
au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
if (((dev->board.tuner_type == TUNER_XC5000) ||
(dev->board.tuner_type == TUNER_XC5000C)) &&
(dev->board.tuner_addr == msg->addr)) {
/*
* Due to I2C clock stretch, we need to use a lower speed
* on xc5000 for commands. However, firmware transfer can
* speed up to 400 KHz.
*/
if (msg->len == 64)
i2c_speed = AU0828_I2C_CLK_250KHZ;
else
i2c_speed = AU0828_I2C_CLK_20KHZ;
}
/* Set the I2C clock */
au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, i2c_speed);
/* 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;
u8 i2c_speed = dev->board.i2c_clk_divider;
int i;
dprintk(4, "%s()\n", __func__);
au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01);
/*
* Due to xc5000c clock stretch, we cannot use full speed at
* readings from xc5000, as otherwise they'll fail.
*/
if (((dev->board.tuner_type == TUNER_XC5000) ||
(dev->board.tuner_type == TUNER_XC5000C)) &&
(dev->board.tuner_addr == msg->addr))
i2c_speed = AU0828_I2C_CLK_20KHZ;
/* Set the I2C clock */
au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, i2c_speed);
/* 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 = KBUILD_MODNAME,
.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;
pr_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, KBUILD_MODNAME,
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) {
pr_info("i2c bus registered\n");
if (i2c_scan)
do_i2c_scan(KBUILD_MODNAME, &dev->i2c_client);
} else
pr_info("i2c bus register FAILED\n");
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,406 @@
/*
handle au0828 IR remotes via linux kernel input layer.
Copyright (C) 2014 Mauro Carvalho Chehab <mchehab@samsung.com>
Copyright (c) 2014 Samsung Electronics Co., Ltd.
Based on em28xx-input.c.
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.
*/
#include "au0828.h"
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/usb.h>
#include <linux/slab.h>
#include <media/rc-core.h>
static int disable_ir;
module_param(disable_ir, int, 0444);
MODULE_PARM_DESC(disable_ir, "disable infrared remote support");
struct au0828_rc {
struct au0828_dev *dev;
struct rc_dev *rc;
char name[32];
char phys[32];
/* poll decoder */
int polling;
struct delayed_work work;
/* i2c slave address of external device (if used) */
u16 i2c_dev_addr;
int (*get_key_i2c)(struct au0828_rc *ir);
};
/*
* AU8522 has a builtin IR receiver. Add functions to get IR from it
*/
static int au8522_rc_write(struct au0828_rc *ir, u16 reg, u8 data)
{
int rc;
char buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
struct i2c_msg msg = { .addr = ir->i2c_dev_addr, .flags = 0,
.buf = buf, .len = sizeof(buf) };
rc = i2c_transfer(ir->dev->i2c_client.adapter, &msg, 1);
if (rc < 0)
return rc;
return (rc == 1) ? 0 : -EIO;
}
static int au8522_rc_read(struct au0828_rc *ir, u16 reg, int val,
char *buf, int size)
{
int rc;
char obuf[3];
struct i2c_msg msg[2] = { { .addr = ir->i2c_dev_addr, .flags = 0,
.buf = obuf, .len = 2 },
{ .addr = ir->i2c_dev_addr, .flags = I2C_M_RD,
.buf = buf, .len = size } };
obuf[0] = 0x40 | reg >> 8;
obuf[1] = reg & 0xff;
if (val >= 0) {
obuf[2] = val;
msg[0].len++;
}
rc = i2c_transfer(ir->dev->i2c_client.adapter, msg, 2);
if (rc < 0)
return rc;
return (rc == 2) ? 0 : -EIO;
}
static int au8522_rc_andor(struct au0828_rc *ir, u16 reg, u8 mask, u8 value)
{
int rc;
char buf, oldbuf;
rc = au8522_rc_read(ir, reg, -1, &buf, 1);
if (rc < 0)
return rc;
oldbuf = buf;
buf = (buf & ~mask) | (value & mask);
/* Nothing to do, just return */
if (buf == oldbuf)
return 0;
return au8522_rc_write(ir, reg, buf);
}
#define au8522_rc_set(ir, reg, bit) au8522_rc_andor(ir, (reg), (bit), (bit))
#define au8522_rc_clear(ir, reg, bit) au8522_rc_andor(ir, (reg), (bit), 0)
/* Remote Controller time units */
#define AU8522_UNIT 200000 /* ns */
#define NEC_START_SPACE (4500000 / AU8522_UNIT)
#define NEC_START_PULSE (562500 * 16)
#define RC5_START_SPACE (4 * AU8522_UNIT)
#define RC5_START_PULSE 888888
static int au0828_get_key_au8522(struct au0828_rc *ir)
{
unsigned char buf[40];
DEFINE_IR_RAW_EVENT(rawir);
int i, j, rc;
int prv_bit, bit, width;
bool first = true;
/* Check IR int */
rc = au8522_rc_read(ir, 0xe1, -1, buf, 1);
if (rc < 0 || !(buf[0] & (1 << 4))) {
/* Be sure that IR is enabled */
au8522_rc_set(ir, 0xe0, 1 << 4);
return 0;
}
/* Something arrived. Get the data */
rc = au8522_rc_read(ir, 0xe3, 0x11, buf, sizeof(buf));
if (rc < 0)
return rc;
/* Disable IR */
au8522_rc_clear(ir, 0xe0, 1 << 4);
/* Enable IR */
au8522_rc_set(ir, 0xe0, 1 << 4);
dprintk(16, "RC data received: %*ph\n", 40, buf);
prv_bit = (buf[0] >> 7) & 0x01;
width = 0;
for (i = 0; i < sizeof(buf); i++) {
for (j = 7; j >= 0; j--) {
bit = (buf[i] >> j) & 0x01;
if (bit == prv_bit) {
width++;
continue;
}
/*
* Fix an au8522 bug: the first pulse event
* is lost. So, we need to fake it, based on the
* protocol. That means that not all raw decoders
* will work, as we need to add a hack for each
* protocol, based on the first space.
* So, we only support RC5 and NEC.
*/
if (first) {
first = false;
init_ir_raw_event(&rawir);
rawir.pulse = true;
if (width > NEC_START_SPACE - 2 &&
width < NEC_START_SPACE + 2) {
/* NEC protocol */
rawir.duration = NEC_START_PULSE;
dprintk(16, "Storing NEC start %s with duration %d",
rawir.pulse ? "pulse" : "space",
rawir.duration);
} else {
/* RC5 protocol */
rawir.duration = RC5_START_PULSE;
dprintk(16, "Storing RC5 start %s with duration %d",
rawir.pulse ? "pulse" : "space",
rawir.duration);
}
ir_raw_event_store(ir->rc, &rawir);
}
init_ir_raw_event(&rawir);
rawir.pulse = prv_bit ? false : true;
rawir.duration = AU8522_UNIT * width;
dprintk(16, "Storing %s with duration %d",
rawir.pulse ? "pulse" : "space",
rawir.duration);
ir_raw_event_store(ir->rc, &rawir);
width = 1;
prv_bit = bit;
}
}
init_ir_raw_event(&rawir);
rawir.pulse = prv_bit ? false : true;
rawir.duration = AU8522_UNIT * width;
dprintk(16, "Storing end %s with duration %d",
rawir.pulse ? "pulse" : "space",
rawir.duration);
ir_raw_event_store(ir->rc, &rawir);
ir_raw_event_handle(ir->rc);
return 1;
}
/*
* Generic IR code
*/
static void au0828_rc_work(struct work_struct *work)
{
struct au0828_rc *ir = container_of(work, struct au0828_rc, work.work);
int rc;
rc = ir->get_key_i2c(ir);
if (rc < 0)
pr_info("Error while getting RC scancode\n");
schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
}
static int au0828_rc_start(struct rc_dev *rc)
{
struct au0828_rc *ir = rc->priv;
INIT_DELAYED_WORK(&ir->work, au0828_rc_work);
/* Enable IR */
au8522_rc_set(ir, 0xe0, 1 << 4);
schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
return 0;
}
static void au0828_rc_stop(struct rc_dev *rc)
{
struct au0828_rc *ir = rc->priv;
cancel_delayed_work_sync(&ir->work);
/* Disable IR */
au8522_rc_clear(ir, 0xe0, 1 << 4);
}
static int au0828_probe_i2c_ir(struct au0828_dev *dev)
{
int i = 0;
const unsigned short addr_list[] = {
0x47, I2C_CLIENT_END
};
while (addr_list[i] != I2C_CLIENT_END) {
if (i2c_probe_func_quick_read(dev->i2c_client.adapter,
addr_list[i]) == 1)
return addr_list[i];
i++;
}
return -ENODEV;
}
int au0828_rc_register(struct au0828_dev *dev)
{
struct au0828_rc *ir;
struct rc_dev *rc;
int err = -ENOMEM;
u16 i2c_rc_dev_addr = 0;
if (!dev->board.has_ir_i2c || disable_ir)
return 0;
i2c_rc_dev_addr = au0828_probe_i2c_ir(dev);
if (!i2c_rc_dev_addr)
return -ENODEV;
ir = kzalloc(sizeof(*ir), GFP_KERNEL);
rc = rc_allocate_device();
if (!ir || !rc)
goto error;
/* record handles to ourself */
ir->dev = dev;
dev->ir = ir;
ir->rc = rc;
rc->priv = ir;
rc->open = au0828_rc_start;
rc->close = au0828_rc_stop;
if (dev->board.has_ir_i2c) { /* external i2c device */
switch (dev->boardnr) {
case AU0828_BOARD_HAUPPAUGE_HVR950Q:
rc->map_name = RC_MAP_HAUPPAUGE;
ir->get_key_i2c = au0828_get_key_au8522;
break;
default:
err = -ENODEV;
goto error;
}
ir->i2c_dev_addr = i2c_rc_dev_addr;
}
/* This is how often we ask the chip for IR information */
ir->polling = 100; /* ms */
/* init input device */
snprintf(ir->name, sizeof(ir->name), "au0828 IR (%s)",
dev->board.name);
usb_make_path(dev->usbdev, ir->phys, sizeof(ir->phys));
strlcat(ir->phys, "/input0", sizeof(ir->phys));
rc->input_name = ir->name;
rc->input_phys = ir->phys;
rc->input_id.bustype = BUS_USB;
rc->input_id.version = 1;
rc->input_id.vendor = le16_to_cpu(dev->usbdev->descriptor.idVendor);
rc->input_id.product = le16_to_cpu(dev->usbdev->descriptor.idProduct);
rc->dev.parent = &dev->usbdev->dev;
rc->driver_name = "au0828-input";
rc->driver_type = RC_DRIVER_IR_RAW;
rc->allowed_protocols = RC_BIT_NEC | RC_BIT_RC5;
/* all done */
err = rc_register_device(rc);
if (err)
goto error;
pr_info("Remote controller %s initalized\n", ir->name);
return 0;
error:
dev->ir = NULL;
rc_free_device(rc);
kfree(ir);
return err;
}
void au0828_rc_unregister(struct au0828_dev *dev)
{
struct au0828_rc *ir = dev->ir;
/* skip detach on non attached boards */
if (!ir)
return;
if (ir->rc)
rc_unregister_device(ir->rc);
/* done */
kfree(ir);
dev->ir = NULL;
}
int au0828_rc_suspend(struct au0828_dev *dev)
{
struct au0828_rc *ir = dev->ir;
if (!ir)
return 0;
pr_info("Stopping RC\n");
cancel_delayed_work_sync(&ir->work);
/* Disable IR */
au8522_rc_clear(ir, 0xe0, 1 << 4);
return 0;
}
int au0828_rc_resume(struct au0828_dev *dev)
{
struct au0828_rc *ir = dev->ir;
if (!ir)
return 0;
pr_info("Restarting RC\n");
/* Enable IR */
au8522_rc_set(ir, 0xe0, 1 << 4);
schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
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 "au0828.h"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.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,350 @@
/*
* 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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#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 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;
unsigned char has_ir_i2c:1;
unsigned char has_analog:1;
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;
int start_count;
int stop_count;
int (*set_frontend)(struct dvb_frontend *fe);
};
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
#ifdef CONFIG_VIDEO_AU0828_RC
struct au0828_rc *ir;
#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 */
/* DVB USB / URB Related */
bool urb_streaming, need_urb_start;
struct urb *urbs[URB_COUNT];
/* Preallocated transfer digital transfer buffers */
char *dig_transfer_buffer[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);
#ifdef CONFIG_VIDEO_AU0828_V4L2
void au0828_v4l2_suspend(struct au0828_dev *dev);
void au0828_v4l2_resume(struct au0828_dev *dev);
#else
static inline void au0828_v4l2_suspend(struct au0828_dev *dev) { };
static inline void au0828_v4l2_resume(struct au0828_dev *dev) { };
#endif
/* ----------------------------------------------------------- */
/* au0828-dvb.c */
extern int au0828_dvb_register(struct au0828_dev *dev);
extern void au0828_dvb_unregister(struct au0828_dev *dev);
void au0828_dvb_suspend(struct au0828_dev *dev);
void au0828_dvb_resume(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 pr_fmt(fmt), ## arg);\
} while (0)
/* au0828-input.c */
#ifdef CONFIG_VIDEO_AU0828_RC
extern int au0828_rc_register(struct au0828_dev *dev);
extern void au0828_rc_unregister(struct au0828_dev *dev);
extern int au0828_rc_suspend(struct au0828_dev *dev);
extern int au0828_rc_resume(struct au0828_dev *dev);
#else
static inline int au0828_rc_register(struct au0828_dev *dev) { return 0; }
static inline void au0828_rc_unregister(struct au0828_dev *dev) { }
static inline int au0828_rc_suspend(struct au0828_dev *dev) { return 0; }
static inline int au0828_rc_resume(struct au0828_dev *dev) { return 0; }
#endif