aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/tm6000
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/tm6000')
-rw-r--r--drivers/media/video/tm6000/Kconfig6
-rw-r--r--drivers/media/video/tm6000/tm6000-alsa.c23
-rw-r--r--drivers/media/video/tm6000/tm6000-cards.c61
-rw-r--r--drivers/media/video/tm6000/tm6000-core.c86
-rw-r--r--drivers/media/video/tm6000/tm6000-dvb.c21
-rw-r--r--drivers/media/video/tm6000/tm6000-i2c.c8
-rw-r--r--drivers/media/video/tm6000/tm6000-input.c407
-rw-r--r--drivers/media/video/tm6000/tm6000-regs.h14
-rw-r--r--drivers/media/video/tm6000/tm6000-stds.c89
-rw-r--r--drivers/media/video/tm6000/tm6000-video.c21
-rw-r--r--drivers/media/video/tm6000/tm6000.h3
11 files changed, 389 insertions, 350 deletions
diff --git a/drivers/media/video/tm6000/Kconfig b/drivers/media/video/tm6000/Kconfig
index 114eec8a630a..a43b77abd931 100644
--- a/drivers/media/video/tm6000/Kconfig
+++ b/drivers/media/video/tm6000/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_TM6000
tristate "TV Master TM5600/6000/6010 driver"
- depends on VIDEO_DEV && I2C && INPUT && RC_CORE && USB && EXPERIMENTAL
+ depends on VIDEO_DEV && I2C && INPUT && RC_CORE && USB
select VIDEO_TUNER
select MEDIA_TUNER_XC2028
select MEDIA_TUNER_XC5000
@@ -16,7 +16,7 @@ config VIDEO_TM6000
config VIDEO_TM6000_ALSA
tristate "TV Master TM5600/6000/6010 audio support"
- depends on VIDEO_TM6000 && SND && EXPERIMENTAL
+ depends on VIDEO_TM6000 && SND
select SND_PCM
---help---
This is a video4linux driver for direct (DMA) audio for
@@ -27,7 +27,7 @@ config VIDEO_TM6000_ALSA
config VIDEO_TM6000_DVB
tristate "DVB Support for tm6000 based TV cards"
- depends on VIDEO_TM6000 && DVB_CORE && USB && EXPERIMENTAL
+ depends on VIDEO_TM6000 && DVB_CORE && USB
select DVB_ZL10353
---help---
This adds support for DVB cards based on the tm5600/tm6000 chip.
diff --git a/drivers/media/video/tm6000/tm6000-alsa.c b/drivers/media/video/tm6000/tm6000-alsa.c
index 7d675c72fd47..bd07ec707956 100644
--- a/drivers/media/video/tm6000/tm6000-alsa.c
+++ b/drivers/media/video/tm6000/tm6000-alsa.c
@@ -42,7 +42,7 @@
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
-static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
+static bool enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1};
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled.");
@@ -146,20 +146,21 @@ static int dsp_buffer_alloc(struct snd_pcm_substream *substream, int size)
#define DEFAULT_FIFO_SIZE 4096
static struct snd_pcm_hardware snd_tm6000_digital_hw = {
- .info = SNDRV_PCM_INFO_MMAP |
+ .info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
- .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.period_bytes_min = 64,
.period_bytes_max = 12544,
- .periods_min = 1,
+ .periods_min = 2,
.periods_max = 98,
.buffer_bytes_max = 62720 * 8,
};
@@ -181,6 +182,7 @@ static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream)
chip->substream = substream;
runtime->hw = snd_tm6000_digital_hw;
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
return 0;
_error:
@@ -347,9 +349,13 @@ static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd)
int err = 0;
switch (cmd) {
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
+ case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
case SNDRV_PCM_TRIGGER_START:
atomic_set(&core->stream_started, 1);
break;
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
+ case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
case SNDRV_PCM_TRIGGER_STOP:
atomic_set(&core->stream_started, 0);
break;
@@ -371,6 +377,14 @@ static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream)
return chip->buf_pos;
}
+static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
+ unsigned long offset)
+{
+ void *pageptr = subs->runtime->dma_area + offset;
+
+ return vmalloc_to_page(pageptr);
+}
+
/*
* operators
*/
@@ -383,6 +397,7 @@ static struct snd_pcm_ops snd_tm6000_pcm_ops = {
.prepare = snd_tm6000_prepare,
.trigger = snd_tm6000_card_trigger,
.pointer = snd_tm6000_pointer,
+ .page = snd_pcm_get_vmalloc_page,
};
/*
diff --git a/drivers/media/video/tm6000/tm6000-cards.c b/drivers/media/video/tm6000/tm6000-cards.c
index ec2578a0fdf7..034659b13174 100644
--- a/drivers/media/video/tm6000/tm6000-cards.c
+++ b/drivers/media/video/tm6000/tm6000-cards.c
@@ -351,6 +351,7 @@ static struct tm6000_board tm6000_boards[] = {
.tuner_addr = 0xc2 >> 1,
.demod_addr = 0x1e >> 1,
.type = TM6010,
+ .ir_codes = RC_MAP_HAUPPAUGE,
.caps = {
.has_tuner = 1,
.has_dvb = 1,
@@ -639,6 +640,7 @@ static struct usb_device_id tm6000_id_table[] = {
{ USB_DEVICE(0x6000, 0xdec3), .driver_info = TM6010_BOARD_BEHOLD_VOYAGER_LITE },
{ }
};
+MODULE_DEVICE_TABLE(usb, tm6000_id_table);
/* Control power led for show some activity */
void tm6000_flash_led(struct tm6000_core *dev, u8 state)
@@ -941,6 +943,7 @@ static void tm6000_config_tuner(struct tm6000_core *dev)
case TM6010_BOARD_HAUPPAUGE_900H:
case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
case TM6010_BOARD_TWINHAN_TU501:
+ ctl.max_len = 80;
ctl.fname = "xc3028L-v36.fw";
break;
default:
@@ -1002,6 +1005,7 @@ static int fill_board_specific_data(struct tm6000_core *dev)
/* setup per-model quirks */
switch (dev->model) {
case TM6010_BOARD_TERRATEC_CINERGY_HYBRID_XE:
+ case TM6010_BOARD_HAUPPAUGE_900H:
dev->quirks |= TM6000_QUIRK_NO_USB_DELAY;
break;
@@ -1050,6 +1054,33 @@ static void use_alternative_detection_method(struct tm6000_core *dev)
tm6000_boards[model].name, model);
}
+#if defined(CONFIG_MODULES) && defined(MODULE)
+static void request_module_async(struct work_struct *work)
+{
+ struct tm6000_core *dev = container_of(work, struct tm6000_core,
+ request_module_wk);
+
+ request_module("tm6000-alsa");
+
+ if (dev->caps.has_dvb)
+ request_module("tm6000-dvb");
+}
+
+static void request_modules(struct tm6000_core *dev)
+{
+ INIT_WORK(&dev->request_module_wk, request_module_async);
+ schedule_work(&dev->request_module_wk);
+}
+
+static void flush_request_modules(struct tm6000_core *dev)
+{
+ flush_work_sync(&dev->request_module_wk);
+}
+#else
+#define request_modules(dev)
+#define flush_request_modules(dev)
+#endif /* CONFIG_MODULES */
+
static int tm6000_init_dev(struct tm6000_core *dev)
{
struct v4l2_frequency f;
@@ -1112,6 +1143,8 @@ static int tm6000_init_dev(struct tm6000_core *dev)
tm6000_ir_init(dev);
+ request_modules(dev);
+
mutex_unlock(&dev->lock);
return 0;
@@ -1324,6 +1357,8 @@ static void tm6000_usb_disconnect(struct usb_interface *interface)
printk(KERN_INFO "tm6000: disconnecting %s\n", dev->name);
+ flush_request_modules(dev);
+
tm6000_ir_fini(dev);
if (dev->gpio.power_led) {
@@ -1371,31 +1406,7 @@ static struct usb_driver tm6000_usb_driver = {
.id_table = tm6000_id_table,
};
-static int __init tm6000_module_init(void)
-{
- int result;
-
- printk(KERN_INFO "tm6000" " v4l2 driver version %d.%d.%d loaded\n",
- (TM6000_VERSION >> 16) & 0xff,
- (TM6000_VERSION >> 8) & 0xff, TM6000_VERSION & 0xff);
-
- /* register this driver with the USB subsystem */
- result = usb_register(&tm6000_usb_driver);
- if (result)
- printk(KERN_ERR "tm6000"
- " usb_register failed. Error number %d.\n", result);
-
- return result;
-}
-
-static void __exit tm6000_module_exit(void)
-{
- /* deregister at USB subsystem */
- usb_deregister(&tm6000_usb_driver);
-}
-
-module_init(tm6000_module_init);
-module_exit(tm6000_module_exit);
+module_usb_driver(tm6000_usb_driver);
MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000/TM6010 USB2 adapter");
MODULE_AUTHOR("Mauro Carvalho Chehab");
diff --git a/drivers/media/video/tm6000/tm6000-core.c b/drivers/media/video/tm6000/tm6000-core.c
index 9783616a0da2..22cc0116deb6 100644
--- a/drivers/media/video/tm6000/tm6000-core.c
+++ b/drivers/media/video/tm6000/tm6000-core.c
@@ -38,6 +38,7 @@ int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req,
int ret, i;
unsigned int pipe;
u8 *data = NULL;
+ int delay = 5000;
mutex_lock(&dev->usb_lock);
@@ -88,7 +89,20 @@ int tm6000_read_write_usb(struct tm6000_core *dev, u8 req_type, u8 req,
}
kfree(data);
- msleep(5);
+
+ if (dev->quirks & TM6000_QUIRK_NO_USB_DELAY)
+ delay = 0;
+
+ if (req == REQ_16_SET_GET_I2C_WR1_RDN && !(req_type & USB_DIR_IN)) {
+ unsigned int tsleep;
+ /* Calculate delay time, 14000us for 64 bytes */
+ tsleep = (len * 200) + 200;
+ if (tsleep < delay)
+ tsleep = delay;
+ usleep_range(tsleep, tsleep + 1000);
+ }
+ else if (delay)
+ usleep_range(delay, delay + 1000);
mutex_unlock(&dev->usb_lock);
return ret;
@@ -125,14 +139,14 @@ int tm6000_set_reg_mask(struct tm6000_core *dev, u8 req, u16 value,
u8 new_index;
rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR, req,
- value, index, buf, 1);
+ value, 0, buf, 1);
if (rc < 0)
return rc;
new_index = (buf[0] & ~mask) | (index & mask);
- if (new_index == index)
+ if (new_index == buf[0])
return 0;
return tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR,
@@ -536,16 +550,16 @@ static struct reg_init tm6010_init_tab[] = {
{ TM6010_REQ05_R18_IMASK7, 0x00 },
- { TM6010_REQ07_RD8_IR_LEADER1, 0xaa },
- { TM6010_REQ07_RD8_IR_LEADER0, 0x30 },
- { TM6010_REQ07_RD8_IR_PULSE_CNT1, 0x20 },
- { TM6010_REQ07_RD8_IR_PULSE_CNT0, 0xd0 },
+ { TM6010_REQ07_RDC_IR_LEADER1, 0xaa },
+ { TM6010_REQ07_RDD_IR_LEADER0, 0x30 },
+ { TM6010_REQ07_RDE_IR_PULSE_CNT1, 0x20 },
+ { TM6010_REQ07_RDF_IR_PULSE_CNT0, 0xd0 },
{ REQ_04_EN_DISABLE_MCU_INT, 0x02, 0x00 },
- { TM6010_REQ07_RD8_IR, 0x2f },
+ { TM6010_REQ07_RD8_IR, 0x0f },
/* set remote wakeup key:any key wakeup */
{ TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe },
- { TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0xff },
+ { TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff },
};
int tm6000_init(struct tm6000_core *dev)
@@ -599,55 +613,6 @@ int tm6000_init(struct tm6000_core *dev)
return rc;
}
-int tm6000_reset(struct tm6000_core *dev)
-{
- int pipe;
- int err;
-
- msleep(500);
-
- err = usb_set_interface(dev->udev, dev->isoc_in.bInterfaceNumber, 0);
- if (err < 0) {
- tm6000_err("failed to select interface %d, alt. setting 0\n",
- dev->isoc_in.bInterfaceNumber);
- return err;
- }
-
- err = usb_reset_configuration(dev->udev);
- if (err < 0) {
- tm6000_err("failed to reset configuration\n");
- return err;
- }
-
- if ((dev->quirks & TM6000_QUIRK_NO_USB_DELAY) == 0)
- msleep(5);
-
- /*
- * Not all devices have int_in defined
- */
- if (!dev->int_in.endp)
- return 0;
-
- err = usb_set_interface(dev->udev, dev->isoc_in.bInterfaceNumber, 2);
- if (err < 0) {
- tm6000_err("failed to select interface %d, alt. setting 2\n",
- dev->isoc_in.bInterfaceNumber);
- return err;
- }
-
- msleep(5);
-
- pipe = usb_rcvintpipe(dev->udev,
- dev->int_in.endp->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
-
- err = usb_clear_halt(dev->udev, pipe);
- if (err < 0) {
- tm6000_err("usb_clear_halt failed: %d\n", err);
- return err;
- }
-
- return 0;
-}
int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate)
{
@@ -696,11 +661,13 @@ int tm6000_set_audio_rinput(struct tm6000_core *dev)
if (dev->dev_type == TM6010) {
/* Audio crossbar setting, default SIF1 */
u8 areg_f0;
+ u8 areg_07 = 0x10;
switch (dev->rinput.amux) {
case TM6000_AMUX_SIF1:
case TM6000_AMUX_SIF2:
areg_f0 = 0x03;
+ areg_07 = 0x30;
break;
case TM6000_AMUX_ADC1:
areg_f0 = 0x00;
@@ -720,6 +687,9 @@ int tm6000_set_audio_rinput(struct tm6000_core *dev)
/* Set audio input crossbar */
tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
areg_f0, 0x0f);
+ /* Mux overflow workaround */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL,
+ areg_07, 0xf0);
} else {
u8 areg_eb;
/* Audio setting, default LINE1 */
diff --git a/drivers/media/video/tm6000/tm6000-dvb.c b/drivers/media/video/tm6000/tm6000-dvb.c
index 5e6c129a4beb..e1f3f66e1e63 100644
--- a/drivers/media/video/tm6000/tm6000-dvb.c
+++ b/drivers/media/video/tm6000/tm6000-dvb.c
@@ -89,9 +89,19 @@ static void tm6000_urb_received(struct urb *urb)
int ret;
struct tm6000_core *dev = urb->context;
- if (urb->status != 0)
+ switch (urb->status) {
+ case 0:
+ case -ETIMEDOUT:
+ break;
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ return;
+ default:
print_err_status(dev, 0, urb->status);
- else if (urb->actual_length > 0)
+ }
+
+ if (urb->actual_length > 0)
dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer,
urb->actual_length);
@@ -151,7 +161,7 @@ static int tm6000_start_stream(struct tm6000_core *dev)
printk(KERN_ERR "tm6000: pipe resetted\n");
/* mutex_lock(&tm6000_driver.open_close_mutex); */
- ret = usb_submit_urb(dvb->bulk_urb, GFP_KERNEL);
+ ret = usb_submit_urb(dvb->bulk_urb, GFP_ATOMIC);
/* mutex_unlock(&tm6000_driver.open_close_mutex); */
if (ret) {
@@ -396,6 +406,11 @@ static int dvb_init(struct tm6000_core *dev)
if (!dev->caps.has_dvb)
return 0;
+ if (dev->udev->speed == USB_SPEED_FULL) {
+ printk(KERN_INFO "This USB2.0 device cannot be run on a USB1.1 port. (it lacks a hardware PID filter)\n");
+ return 0;
+ }
+
dvb = kzalloc(sizeof(struct tm6000_dvb), GFP_KERNEL);
if (!dvb) {
printk(KERN_INFO "Cannot allocate memory\n");
diff --git a/drivers/media/video/tm6000/tm6000-i2c.c b/drivers/media/video/tm6000/tm6000-i2c.c
index 0290bbf00c3e..c7e23e3dd75e 100644
--- a/drivers/media/video/tm6000/tm6000-i2c.c
+++ b/drivers/media/video/tm6000/tm6000-i2c.c
@@ -46,11 +46,10 @@ static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr,
__u8 reg, char *buf, int len)
{
int rc;
- unsigned int tsleep;
unsigned int i2c_packet_limit = 16;
if (dev->dev_type == TM6010)
- i2c_packet_limit = 64;
+ i2c_packet_limit = 80;
if (!buf)
return -1;
@@ -71,10 +70,6 @@ static int tm6000_i2c_send_regs(struct tm6000_core *dev, unsigned char addr,
return rc;
}
- /* Calculate delay time, 14000us for 64 bytes */
- tsleep = ((len * 200) + 200 + 1000) / 1000;
- msleep(tsleep);
-
/* release mutex */
return rc;
}
@@ -145,7 +140,6 @@ static int tm6000_i2c_recv_regs16(struct tm6000_core *dev, unsigned char addr,
return rc;
}
- msleep(1400 / 1000);
rc = tm6000_read_write_usb(dev, USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, REQ_35_AFTEK_TUNER_READ,
reg, 0, buf, len);
diff --git a/drivers/media/video/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c
index 405d12729d05..7844607dd45a 100644
--- a/drivers/media/video/tm6000/tm6000-input.c
+++ b/drivers/media/video/tm6000/tm6000-input.c
@@ -31,22 +31,25 @@
static unsigned int ir_debug;
module_param(ir_debug, int, 0644);
-MODULE_PARM_DESC(ir_debug, "enable debug message [IR]");
+MODULE_PARM_DESC(ir_debug, "debug message level");
static unsigned int enable_ir = 1;
module_param(enable_ir, int, 0644);
MODULE_PARM_DESC(enable_ir, "enable ir (default is enable)");
-/* number of 50ms for ON-OFF-ON power led */
-/* show IR activity */
-#define PWLED_OFF 2
+static unsigned int ir_clock_mhz = 12;
+module_param(ir_clock_mhz, int, 0644);
+MODULE_PARM_DESC(enable_ir, "ir clock, in MHz");
+
+#define URB_SUBMIT_DELAY 100 /* ms - Delay to submit an URB request on retrial and init */
+#define URB_INT_LED_DELAY 100 /* ms - Delay to turn led on again on int mode */
#undef dprintk
-#define dprintk(fmt, arg...) \
- if (ir_debug) { \
+#define dprintk(level, fmt, arg...) do {\
+ if (ir_debug >= level) \
printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
- }
+ } while (0)
struct tm6000_ir_poll_result {
u16 rc_data;
@@ -62,20 +65,15 @@ struct tm6000_IR {
int polling;
struct delayed_work work;
u8 wait:1;
- u8 key:1;
- u8 pwled:1;
- u8 pwledcnt;
+ u8 pwled:2;
+ u8 submit_urb:1;
u16 key_addr;
struct urb *int_urb;
- u8 *urb_data;
-
- int (*get_key) (struct tm6000_IR *, struct tm6000_ir_poll_result *);
/* IR device properties */
u64 rc_type;
};
-
void tm6000_ir_wait(struct tm6000_core *dev, u8 state)
{
struct tm6000_IR *ir = dev->ir;
@@ -83,62 +81,84 @@ void tm6000_ir_wait(struct tm6000_core *dev, u8 state)
if (!dev->ir)
return;
+ dprintk(2, "%s: %i\n",__func__, ir->wait);
+
if (state)
ir->wait = 1;
else
ir->wait = 0;
}
-
static int tm6000_ir_config(struct tm6000_IR *ir)
{
struct tm6000_core *dev = ir->dev;
- u8 buf[10];
- int rc;
+ u32 pulse = 0, leader = 0;
+
+ dprintk(2, "%s\n",__func__);
+
+ /*
+ * The IR decoder supports RC-5 or NEC, with a configurable timing.
+ * The timing configuration there is not that accurate, as it uses
+ * approximate values. The NEC spec mentions a 562.5 unit period,
+ * and RC-5 uses a 888.8 period.
+ * Currently, driver assumes a clock provided by a 12 MHz XTAL, but
+ * a modprobe parameter can adjust it.
+ * Adjustments are required for other timings.
+ * It seems that the 900ms timing for NEC is used to detect a RC-5
+ * IR, in order to discard such decoding
+ */
switch (ir->rc_type) {
case RC_TYPE_NEC:
- /* Setup IR decoder for NEC standard 12MHz system clock */
- /* IR_LEADER_CNT = 0.9ms */
- tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_LEADER1, 0xaa);
- tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_LEADER0, 0x30);
- /* IR_PULSE_CNT = 0.7ms */
- tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT1, 0x20);
- tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_PULSE_CNT0, 0xd0);
- /* Remote WAKEUP = enable */
- tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe);
- /* IR_WKUP_SEL = Low byte in decoded IR data */
- tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_SEL, 0xff);
- /* IR_WKU_ADD code */
- tm6000_set_reg(dev, TM6010_REQ07_RD8_IR_WAKEUP_ADD, 0xff);
- tm6000_flash_led(dev, 0);
- msleep(100);
- tm6000_flash_led(dev, 1);
+ leader = 900; /* ms */
+ pulse = 700; /* ms - the actual value would be 562 */
break;
default:
- /* hack */
- buf[0] = 0xff;
- buf[1] = 0xff;
- buf[2] = 0xf2;
- buf[3] = 0x2b;
- buf[4] = 0x20;
- buf[5] = 0x35;
- buf[6] = 0x60;
- buf[7] = 0x04;
- buf[8] = 0xc0;
- buf[9] = 0x08;
-
- rc = tm6000_read_write_usb(dev, USB_DIR_OUT | USB_TYPE_VENDOR |
- USB_RECIP_DEVICE, REQ_00_SET_IR_VALUE, 0, 0, buf, 0x0a);
- msleep(100);
-
- if (rc < 0) {
- printk(KERN_INFO "IR configuration failed");
- return rc;
- }
+ case RC_TYPE_RC5:
+ leader = 900; /* ms - from the NEC decoding */
+ pulse = 1780; /* ms - The actual value would be 1776 */
break;
}
+ pulse = ir_clock_mhz * pulse;
+ leader = ir_clock_mhz * leader;
+ if (ir->rc_type == RC_TYPE_NEC)
+ leader = leader | 0x8000;
+
+ dprintk(2, "%s: %s, %d MHz, leader = 0x%04x, pulse = 0x%06x \n",
+ __func__,
+ (ir->rc_type == RC_TYPE_NEC) ? "NEC" : "RC-5",
+ ir_clock_mhz, leader, pulse);
+
+ /* Remote WAKEUP = enable, normal mode, from IR decoder output */
+ tm6000_set_reg(dev, TM6010_REQ07_RE5_REMOTE_WAKEUP, 0xfe);
+
+ /* Enable IR reception on non-busrt mode */
+ tm6000_set_reg(dev, TM6010_REQ07_RD8_IR, 0x2f);
+
+ /* IR_WKUP_SEL = Low byte in decoded IR data */
+ tm6000_set_reg(dev, TM6010_REQ07_RDA_IR_WAKEUP_SEL, 0xff);
+ /* IR_WKU_ADD code */
+ tm6000_set_reg(dev, TM6010_REQ07_RDB_IR_WAKEUP_ADD, 0xff);
+
+ tm6000_set_reg(dev, TM6010_REQ07_RDC_IR_LEADER1, leader >> 8);
+ tm6000_set_reg(dev, TM6010_REQ07_RDD_IR_LEADER0, leader);
+
+ tm6000_set_reg(dev, TM6010_REQ07_RDE_IR_PULSE_CNT1, pulse >> 8);
+ tm6000_set_reg(dev, TM6010_REQ07_RDF_IR_PULSE_CNT0, pulse);
+
+ if (!ir->polling)
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0);
+ else
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1);
+ msleep(10);
+
+ /* Shows that IR is working via the LED */
+ tm6000_flash_led(dev, 0);
+ msleep(100);
+ tm6000_flash_led(dev, 1);
+ ir->pwled = 1;
+
return 0;
}
@@ -146,132 +166,124 @@ static void tm6000_ir_urb_received(struct urb *urb)
{
struct tm6000_core *dev = urb->context;
struct tm6000_IR *ir = dev->ir;
+ struct tm6000_ir_poll_result poll_result;
+ char *buf;
int rc;
- if (urb->status != 0)
- printk(KERN_INFO "not ready\n");
- else if (urb->actual_length > 0) {
- memcpy(ir->urb_data, urb->transfer_buffer, urb->actual_length);
+ dprintk(2, "%s\n",__func__);
+ if (urb->status < 0 || urb->actual_length <= 0) {
+ printk(KERN_INFO "tm6000: IR URB failure: status: %i, length %i\n",
+ urb->status, urb->actual_length);
+ ir->submit_urb = 1;
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY));
+ return;
+ }
+ buf = urb->transfer_buffer;
- dprintk("data %02x %02x %02x %02x\n", ir->urb_data[0],
- ir->urb_data[1], ir->urb_data[2], ir->urb_data[3]);
+ if (ir_debug)
+ print_hex_dump(KERN_DEBUG, "tm6000: IR data: ",
+ DUMP_PREFIX_OFFSET,16, 1,
+ buf, urb->actual_length, false);
- ir->key = 1;
- }
+ poll_result.rc_data = buf[0];
+ if (urb->actual_length > 1)
+ poll_result.rc_data |= buf[1] << 8;
+
+ dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data);
+ rc_keydown(ir->rc, poll_result.rc_data, 0);
rc = usb_submit_urb(urb, GFP_ATOMIC);
+ /*
+ * Flash the led. We can't do it here, as it is running on IRQ context.
+ * So, use the scheduler to do it, in a few ms.
+ */
+ ir->pwled = 2;
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(10));
}
-static int default_polling_getkey(struct tm6000_IR *ir,
- struct tm6000_ir_poll_result *poll_result)
+static void tm6000_ir_handle_key(struct work_struct *work)
{
+ struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work);
struct tm6000_core *dev = ir->dev;
+ struct tm6000_ir_poll_result poll_result;
int rc;
u8 buf[2];
- if (ir->wait && !&dev->int_in)
- return 0;
-
- if (&dev->int_in) {
- switch (ir->rc_type) {
- case RC_TYPE_RC5:
- poll_result->rc_data = ir->urb_data[0];
- break;
- case RC_TYPE_NEC:
- if (ir->urb_data[1] == ((ir->key_addr >> 8) & 0xff)) {
- poll_result->rc_data = ir->urb_data[0]
- | ir->urb_data[1] << 8;
- }
- break;
- default:
- poll_result->rc_data = ir->urb_data[0]
- | ir->urb_data[1] << 8;
- break;
- }
- } else {
- tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0);
- msleep(10);
- tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 1);
- msleep(10);
-
- if (ir->rc_type == RC_TYPE_RC5) {
- rc = tm6000_read_write_usb(dev, USB_DIR_IN |
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- REQ_02_GET_IR_CODE, 0, 0, buf, 1);
-
- msleep(10);
-
- dprintk("read data=%02x\n", buf[0]);
- if (rc < 0)
- return rc;
+ if (ir->wait)
+ return;
- poll_result->rc_data = buf[0];
- } else {
- rc = tm6000_read_write_usb(dev, USB_DIR_IN |
- USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- REQ_02_GET_IR_CODE, 0, 0, buf, 2);
+ dprintk(3, "%s\n",__func__);
- msleep(10);
+ rc = tm6000_read_write_usb(dev, USB_DIR_IN |
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ REQ_02_GET_IR_CODE, 0, 0, buf, 2);
+ if (rc < 0)
+ return;
- dprintk("read data=%04x\n", buf[0] | buf[1] << 8);
- if (rc < 0)
- return rc;
+ if (rc > 1)
+ poll_result.rc_data = buf[0] | buf[1] << 8;
+ else
+ poll_result.rc_data = buf[0];
- poll_result->rc_data = buf[0] | buf[1] << 8;
+ /* Check if something was read */
+ if ((poll_result.rc_data & 0xff) == 0xff) {
+ if (!ir->pwled) {
+ tm6000_flash_led(dev, 1);
+ ir->pwled = 1;
}
- if ((poll_result->rc_data & 0x00ff) != 0xff)
- ir->key = 1;
+ return;
}
- return 0;
+
+ dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data);
+ rc_keydown(ir->rc, poll_result.rc_data, 0);
+ tm6000_flash_led(dev, 0);
+ ir->pwled = 0;
+
+ /* Re-schedule polling */
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
}
-static void tm6000_ir_handle_key(struct tm6000_IR *ir)
+static void tm6000_ir_int_work(struct work_struct *work)
{
+ struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work);
struct tm6000_core *dev = ir->dev;
- int result;
- struct tm6000_ir_poll_result poll_result;
+ int rc;
- /* read the registers containing the IR status */
- result = ir->get_key(ir, &poll_result);
- if (result < 0) {
- printk(KERN_INFO "ir->get_key() failed %d\n", result);
- return;
- }
+ dprintk(3, "%s, submit_urb = %d, pwled = %d\n",__func__, ir->submit_urb,
+ ir->pwled);
- dprintk("ir->get_key result data=%04x\n", poll_result.rc_data);
+ if (ir->submit_urb) {
+ dprintk(3, "Resubmit urb\n");
+ tm6000_set_reg(dev, REQ_04_EN_DISABLE_MCU_INT, 2, 0);
- if (ir->pwled) {
- if (ir->pwledcnt >= PWLED_OFF) {
- ir->pwled = 0;
- ir->pwledcnt = 0;
- tm6000_flash_led(dev, 1);
- } else
- ir->pwledcnt += 1;
+ rc = usb_submit_urb(ir->int_urb, GFP_ATOMIC);
+ if (rc < 0) {
+ printk(KERN_ERR "tm6000: Can't submit an IR interrupt. Error %i\n",
+ rc);
+ /* Retry in 100 ms */
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY));
+ return;
+ }
+ ir->submit_urb = 0;
}
- if (ir->key) {
- rc_keydown(ir->rc, poll_result.rc_data, 0);
- ir->key = 0;
- ir->pwled = 1;
- ir->pwledcnt = 0;
+ /* Led is enabled only if USB submit doesn't fail */
+ if (ir->pwled == 2) {
tm6000_flash_led(dev, 0);
+ ir->pwled = 0;
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_INT_LED_DELAY));
+ } else if (!ir->pwled) {
+ tm6000_flash_led(dev, 1);
+ ir->pwled = 1;
}
- return;
-}
-
-static void tm6000_ir_work(struct work_struct *work)
-{
- struct tm6000_IR *ir = container_of(work, struct tm6000_IR, work.work);
-
- tm6000_ir_handle_key(ir);
- schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
}
static int tm6000_ir_start(struct rc_dev *rc)
{
struct tm6000_IR *ir = rc->priv;
- INIT_DELAYED_WORK(&ir->work, tm6000_ir_work);
+ dprintk(2, "%s\n",__func__);
+
schedule_delayed_work(&ir->work, 0);
return 0;
@@ -281,6 +293,8 @@ static void tm6000_ir_stop(struct rc_dev *rc)
{
struct tm6000_IR *ir = rc->priv;
+ dprintk(2, "%s\n",__func__);
+
cancel_delayed_work_sync(&ir->work);
}
@@ -291,10 +305,11 @@ static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
if (!ir)
return 0;
+ dprintk(2, "%s\n",__func__);
+
if ((rc->rc_map.scan) && (rc_type == RC_TYPE_NEC))
ir->key_addr = ((rc->rc_map.scan[0].scancode >> 8) & 0xffff);
- ir->get_key = default_polling_getkey;
ir->rc_type = rc_type;
tm6000_ir_config(ir);
@@ -302,17 +317,19 @@ static int tm6000_ir_change_protocol(struct rc_dev *rc, u64 rc_type)
return 0;
}
-int tm6000_ir_int_start(struct tm6000_core *dev)
+static int __tm6000_ir_int_start(struct rc_dev *rc)
{
- struct tm6000_IR *ir = dev->ir;
+ struct tm6000_IR *ir = rc->priv;
+ struct tm6000_core *dev = ir->dev;
int pipe, size;
int err = -ENOMEM;
-
if (!ir)
return -ENODEV;
- ir->int_urb = usb_alloc_urb(0, GFP_KERNEL);
+ dprintk(2, "%s\n",__func__);
+
+ ir->int_urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!ir->int_urb)
return -ENOMEM;
@@ -321,42 +338,59 @@ int tm6000_ir_int_start(struct tm6000_core *dev)
& USB_ENDPOINT_NUMBER_MASK);
size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
- dprintk("IR max size: %d\n", size);
+ dprintk(1, "IR max size: %d\n", size);
- ir->int_urb->transfer_buffer = kzalloc(size, GFP_KERNEL);
+ ir->int_urb->transfer_buffer = kzalloc(size, GFP_ATOMIC);
if (ir->int_urb->transfer_buffer == NULL) {
usb_free_urb(ir->int_urb);
return err;
}
- dprintk("int interval: %d\n", dev->int_in.endp->desc.bInterval);
+ dprintk(1, "int interval: %d\n", dev->int_in.endp->desc.bInterval);
+
usb_fill_int_urb(ir->int_urb, dev->udev, pipe,
ir->int_urb->transfer_buffer, size,
tm6000_ir_urb_received, dev,
dev->int_in.endp->desc.bInterval);
- err = usb_submit_urb(ir->int_urb, GFP_KERNEL);
- if (err) {
- kfree(ir->int_urb->transfer_buffer);
- usb_free_urb(ir->int_urb);
- return err;
- }
- ir->urb_data = kzalloc(size, GFP_KERNEL);
+
+ ir->submit_urb = 1;
+ schedule_delayed_work(&ir->work, msecs_to_jiffies(URB_SUBMIT_DELAY));
return 0;
}
-void tm6000_ir_int_stop(struct tm6000_core *dev)
+static void __tm6000_ir_int_stop(struct rc_dev *rc)
{
- struct tm6000_IR *ir = dev->ir;
+ struct tm6000_IR *ir = rc->priv;
- if (!ir)
+ if (!ir || !ir->int_urb)
return;
+ dprintk(2, "%s\n",__func__);
+
usb_kill_urb(ir->int_urb);
kfree(ir->int_urb->transfer_buffer);
usb_free_urb(ir->int_urb);
ir->int_urb = NULL;
- kfree(ir->urb_data);
- ir->urb_data = NULL;
+}
+
+int tm6000_ir_int_start(struct tm6000_core *dev)
+{
+ struct tm6000_IR *ir = dev->ir;
+
+ if (!ir)
+ return 0;
+
+ return __tm6000_ir_int_start(ir->rc);
+}
+
+void tm6000_ir_int_stop(struct tm6000_core *dev)
+{
+ struct tm6000_IR *ir = dev->ir;
+
+ if (!ir || !ir->rc)
+ return;
+
+ __tm6000_ir_int_stop(ir->rc);
}
int tm6000_ir_init(struct tm6000_core *dev)
@@ -374,29 +408,36 @@ int tm6000_ir_init(struct tm6000_core *dev)
if (!dev->ir_codes)
return 0;
- ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+ ir = kzalloc(sizeof(*ir), GFP_ATOMIC);
rc = rc_allocate_device();
if (!ir || !rc)
goto out;
+ dprintk(2, "%s\n", __func__);
+
/* record handles to ourself */
ir->dev = dev;
dev->ir = ir;
ir->rc = rc;
- /* input einrichten */
+ /* input setup */
rc->allowed_protos = RC_TYPE_RC5 | RC_TYPE_NEC;
+ /* Neded, in order to support NEC remotes with 24 or 32 bits */
+ rc->scanmask = 0xffff;
rc->priv = ir;
rc->change_protocol = tm6000_ir_change_protocol;
- rc->open = tm6000_ir_start;
- rc->close = tm6000_ir_stop;
+ if (dev->int_in.endp) {
+ rc->open = __tm6000_ir_int_start;
+ rc->close = __tm6000_ir_int_stop;
+ INIT_DELAYED_WORK(&ir->work, tm6000_ir_int_work);
+ } else {
+ rc->open = tm6000_ir_start;
+ rc->close = tm6000_ir_stop;
+ ir->polling = 50;
+ INIT_DELAYED_WORK(&ir->work, tm6000_ir_handle_key);
+ }
rc->driver_type = RC_DRIVER_SCANCODE;
- ir->polling = 50;
- ir->pwled = 0;
- ir->pwledcnt = 0;
-
-
snprintf(ir->name, sizeof(ir->name), "tm5600/60x0 IR (%s)",
dev->name);
@@ -415,15 +456,6 @@ int tm6000_ir_init(struct tm6000_core *dev)
rc->driver_name = "tm6000";
rc->dev.parent = &dev->udev->dev;
- if (&dev->int_in) {
- dprintk("IR over int\n");
-
- err = tm6000_ir_int_start(dev);
-
- if (err)
- goto out;
- }
-
/* ir register */
err = rc_register_device(rc);
if (err)
@@ -447,10 +479,19 @@ int tm6000_ir_fini(struct tm6000_core *dev)
if (!ir)
return 0;
+ dprintk(2, "%s\n",__func__);
+
rc_unregister_device(ir->rc);
- if (ir->int_urb)
- tm6000_ir_int_stop(dev);
+ if (!ir->polling)
+ __tm6000_ir_int_stop(ir->rc);
+
+ tm6000_ir_stop(ir->rc);
+
+ /* Turn off the led */
+ tm6000_flash_led(dev, 0);
+ ir->pwled = 0;
+
kfree(ir);
dev->ir = NULL;
diff --git a/drivers/media/video/tm6000/tm6000-regs.h b/drivers/media/video/tm6000/tm6000-regs.h
index 7f491b6de933..a38c251ed57b 100644
--- a/drivers/media/video/tm6000/tm6000-regs.h
+++ b/drivers/media/video/tm6000/tm6000-regs.h
@@ -284,19 +284,19 @@ enum {
/* ONLY for TM6010 */
#define TM6010_REQ07_RD8_IR 0x07, 0xd8
/* ONLY for TM6010 */
-#define TM6010_REQ07_RD8_IR_BSIZE 0x07, 0xd9
+#define TM6010_REQ07_RD9_IR_BSIZE 0x07, 0xd9
/* ONLY for TM6010 */
-#define TM6010_REQ07_RD8_IR_WAKEUP_SEL 0x07, 0xda
+#define TM6010_REQ07_RDA_IR_WAKEUP_SEL 0x07, 0xda
/* ONLY for TM6010 */
-#define TM6010_REQ07_RD8_IR_WAKEUP_ADD 0x07, 0xdb
+#define TM6010_REQ07_RDB_IR_WAKEUP_ADD 0x07, 0xdb
/* ONLY for TM6010 */
-#define TM6010_REQ07_RD8_IR_LEADER1 0x07, 0xdc
+#define TM6010_REQ07_RDC_IR_LEADER1 0x07, 0xdc
/* ONLY for TM6010 */
-#define TM6010_REQ07_RD8_IR_LEADER0 0x07, 0xdd
+#define TM6010_REQ07_RDD_IR_LEADER0 0x07, 0xdd
/* ONLY for TM6010 */
-#define TM6010_REQ07_RD8_IR_PULSE_CNT1 0x07, 0xde
+#define TM6010_REQ07_RDE_IR_PULSE_CNT1 0x07, 0xde
/* ONLY for TM6010 */
-#define TM6010_REQ07_RD8_IR_PULSE_CNT0 0x07, 0xdf
+#define TM6010_REQ07_RDF_IR_PULSE_CNT0 0x07, 0xdf
/* ONLY for TM6010 */
#define TM6010_REQ07_RE0_DVIDEO_SOURCE 0x07, 0xe0
/* ONLY for TM6010 */
diff --git a/drivers/media/video/tm6000/tm6000-stds.c b/drivers/media/video/tm6000/tm6000-stds.c
index 9a4145dc3d87..9dc0831d813f 100644
--- a/drivers/media/video/tm6000/tm6000-stds.c
+++ b/drivers/media/video/tm6000/tm6000-stds.c
@@ -361,82 +361,51 @@ static int tm6000_set_audio_std(struct tm6000_core *dev)
return 0;
}
- switch (tm6010_a_mode) {
+ /*
+ * STD/MN shouldn't be affected by tm6010_a_mode, as there's just one
+ * audio standard for each V4L2_STD type.
+ */
+ if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_KR) {
+ areg_05 |= 0x04;
+ } else if ((dev->norm & V4L2_STD_NTSC) == V4L2_STD_NTSC_M_JP) {
+ areg_05 |= 0x43;
+ } else if (dev->norm & V4L2_STD_MN) {
+ areg_05 |= 0x22;
+ } else switch (tm6010_a_mode) {
/* auto */
case 0:
- switch (dev->norm) {
- case V4L2_STD_NTSC_M_KR:
+ if ((dev->norm & V4L2_STD_SECAM) == V4L2_STD_SECAM_L)
areg_05 |= 0x00;
- break;
- case V4L2_STD_NTSC_M_JP:
- areg_05 |= 0x40;
- break;
- case V4L2_STD_NTSC_M:
- case V4L2_STD_PAL_M:
- case V4L2_STD_PAL_N:
- areg_05 |= 0x20;
- break;
- case V4L2_STD_PAL_Nc:
- areg_05 |= 0x60;
- break;
- case V4L2_STD_SECAM_L:
- areg_05 |= 0x00;
- break;
- case V4L2_STD_DK:
+ else /* Other PAL/SECAM standards */
areg_05 |= 0x10;
- break;
- }
break;
/* A2 */
case 1:
- switch (dev->norm) {
- case V4L2_STD_B:
- case V4L2_STD_GH:
- areg_05 = 0x05;
- break;
- case V4L2_STD_DK:
+ if (dev->norm & V4L2_STD_DK)
areg_05 = 0x09;
- break;
- }
+ else
+ areg_05 = 0x05;
break;
/* NICAM */
case 2:
- switch (dev->norm) {
- case V4L2_STD_B:
- case V4L2_STD_GH:
- areg_05 = 0x07;
- break;
- case V4L2_STD_DK:
+ if (dev->norm & V4L2_STD_DK) {
areg_05 = 0x06;
- break;
- case V4L2_STD_PAL_I:
+ } else if (dev->norm & V4L2_STD_PAL_I) {
areg_05 = 0x08;
- break;
- case V4L2_STD_SECAM_L:
+ } else if (dev->norm & V4L2_STD_SECAM_L) {
areg_05 = 0x0a;
areg_02 = 0x02;
- break;
+ } else {
+ areg_05 = 0x07;
}
nicam_flag = 1;
break;
/* other */
case 3:
- switch (dev->norm) {
- /* DK3_A2 */
- case V4L2_STD_DK:
+ if (dev->norm & V4L2_STD_DK) {
areg_05 = 0x0b;
- break;
- /* Korea */
- case V4L2_STD_NTSC_M_KR:
- areg_05 = 0x04;
- break;
- /* EIAJ */
- case V4L2_STD_NTSC_M_JP:
- areg_05 = 0x03;
- break;
- default:
+ } else {
areg_05 = 0x02;
- break;
}
break;
}
@@ -557,10 +526,16 @@ int tm6000_set_standard(struct tm6000_core *dev)
case TM6000_AMUX_ADC1:
tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
0x00, 0x0f);
+ /* Mux overflow workaround */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL,
+ 0x10, 0xf0);
break;
case TM6000_AMUX_ADC2:
tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
0x08, 0x0f);
+ /* Mux overflow workaround */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL,
+ 0x10, 0xf0);
break;
case TM6000_AMUX_SIF1:
reg_08_e2 |= 0x02;
@@ -570,6 +545,9 @@ int tm6000_set_standard(struct tm6000_core *dev)
tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf3);
tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
0x02, 0x0f);
+ /* Mux overflow workaround */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL,
+ 0x30, 0xf0);
break;
case TM6000_AMUX_SIF2:
reg_08_e2 |= 0x02;
@@ -579,6 +557,9 @@ int tm6000_set_standard(struct tm6000_core *dev)
tm6000_set_reg(dev, TM6010_REQ08_RE4_ADC_IN2_SEL, 0xf7);
tm6000_set_reg_mask(dev, TM6010_REQ08_RF0_DAUDIO_INPUT_CONFIG,
0x02, 0x0f);
+ /* Mux overflow workaround */
+ tm6000_set_reg_mask(dev, TM6010_REQ07_R07_OUTPUT_CONTROL,
+ 0x30, 0xf0);
break;
default:
break;
diff --git a/drivers/media/video/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c
index 1e5ace0b5d10..bc13db736e24 100644
--- a/drivers/media/video/tm6000/tm6000-video.c
+++ b/drivers/media/video/tm6000/tm6000-video.c
@@ -1605,16 +1605,25 @@ static int tm6000_release(struct file *file)
res_free(dev, fh);
if (!dev->users) {
- int err;
-
tm6000_uninit_isoc(dev);
+ /* Stop interrupt USB pipe */
+ tm6000_ir_int_stop(dev);
+
+ usb_reset_configuration(dev->udev);
+
+ if (dev->int_in.endp)
+ usb_set_interface(dev->udev,
+ dev->isoc_in.bInterfaceNumber, 2);
+ else
+ usb_set_interface(dev->udev,
+ dev->isoc_in.bInterfaceNumber, 0);
+
+ /* Start interrupt USB pipe */
+ tm6000_ir_int_start(dev);
+
if (!fh->radio)
videobuf_mmap_free(&fh->vb_vidq);
-
- err = tm6000_reset(dev);
- if (err < 0)
- dev_err(&vdev->dev, "reset failed: %d\n", err);
}
kfree(fh);
diff --git a/drivers/media/video/tm6000/tm6000.h b/drivers/media/video/tm6000/tm6000.h
index 2777e514eff2..27ba659cfa85 100644
--- a/drivers/media/video/tm6000/tm6000.h
+++ b/drivers/media/video/tm6000/tm6000.h
@@ -188,6 +188,9 @@ struct tm6000_core {
/* Device Capabilities*/
struct tm6000_capabilities caps;
+ /* Used to load alsa/dvb */
+ struct work_struct request_module_wk;
+
/* Tuner configuration */
int tuner_type; /* type of the tuner */
int tuner_addr; /* tuner address */