diff options
author | Jiri Kosina <jkosina@suse.cz> | 2012-02-06 16:11:09 +0100 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2012-02-06 16:12:16 +0100 |
commit | d4730ace0c7dbb64513e35b6fc5fd7e7f381e490 (patch) | |
tree | 18890c096cdd869a593c56de3257faa40af8e050 /drivers/media/video/tm6000 | |
parent | c1c454b8691cc95aa83f19273ed7845914c70e83 (diff) | |
parent | 23783f817bceedd6d4e549385e3f400ea64059e5 (diff) | |
download | lwn-d4730ace0c7dbb64513e35b6fc5fd7e7f381e490.tar.gz lwn-d4730ace0c7dbb64513e35b6fc5fd7e7f381e490.zip |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux into upstream-fixes
Sync with Linus' tree. This is necessary to have a base for
patch that fixes commit 35b4c01e29b ("power_supply: add "powers"
links to self-powered HID devices") which went in through Anton's
tree.
Diffstat (limited to 'drivers/media/video/tm6000')
-rw-r--r-- | drivers/media/video/tm6000/Kconfig | 6 | ||||
-rw-r--r-- | drivers/media/video/tm6000/tm6000-alsa.c | 23 | ||||
-rw-r--r-- | drivers/media/video/tm6000/tm6000-cards.c | 35 | ||||
-rw-r--r-- | drivers/media/video/tm6000/tm6000-core.c | 86 | ||||
-rw-r--r-- | drivers/media/video/tm6000/tm6000-dvb.c | 21 | ||||
-rw-r--r-- | drivers/media/video/tm6000/tm6000-i2c.c | 8 | ||||
-rw-r--r-- | drivers/media/video/tm6000/tm6000-input.c | 407 | ||||
-rw-r--r-- | drivers/media/video/tm6000/tm6000-regs.h | 14 | ||||
-rw-r--r-- | drivers/media/video/tm6000/tm6000-stds.c | 89 | ||||
-rw-r--r-- | drivers/media/video/tm6000/tm6000-video.c | 21 | ||||
-rw-r--r-- | drivers/media/video/tm6000/tm6000.h | 3 |
11 files changed, 388 insertions, 325 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 ff939bc0e0b9..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) { 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 */ |