mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-30 07:38:52 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
36
drivers/media/usb/au0828/Kconfig
Normal file
36
drivers/media/usb/au0828/Kconfig
Normal 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.
|
||||
17
drivers/media/usb/au0828/Makefile
Normal file
17
drivers/media/usb/au0828/Makefile
Normal 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)
|
||||
362
drivers/media/usb/au0828/au0828-cards.c
Normal file
362
drivers/media/usb/au0828/au0828-cards.c
Normal 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);
|
||||
27
drivers/media/usb/au0828/au0828-cards.h
Normal file
27
drivers/media/usb/au0828/au0828-cards.h
Normal 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
|
||||
376
drivers/media/usb/au0828/au0828-core.c
Normal file
376
drivers/media/usb/au0828/au0828-core.c
Normal 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");
|
||||
658
drivers/media/usb/au0828/au0828-dvb.c
Normal file
658
drivers/media/usb/au0828/au0828-dvb.c
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
414
drivers/media/usb/au0828/au0828-i2c.c
Normal file
414
drivers/media/usb/au0828/au0828-i2c.c
Normal 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;
|
||||
}
|
||||
|
||||
406
drivers/media/usb/au0828/au0828-input.c
Normal file
406
drivers/media/usb/au0828/au0828-input.c
Normal 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;
|
||||
}
|
||||
66
drivers/media/usb/au0828/au0828-reg.h
Normal file
66
drivers/media/usb/au0828/au0828-reg.h
Normal 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
|
||||
138
drivers/media/usb/au0828/au0828-vbi.c
Normal file
138
drivers/media/usb/au0828/au0828-vbi.c
Normal 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,
|
||||
};
|
||||
2123
drivers/media/usb/au0828/au0828-video.c
Normal file
2123
drivers/media/usb/au0828/au0828-video.c
Normal file
File diff suppressed because it is too large
Load diff
350
drivers/media/usb/au0828/au0828.h
Normal file
350
drivers/media/usb/au0828/au0828.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue