Commit 9eedaf2a authored by Andrew Morton's avatar Andrew Morton Committed by Greg Kroah-Hartman

[PATCH] v4l: saa7134 cleanups and new cards.

From: Gerd Knorr <kraxel@bytesex.org>

This patch is a update for the saa7134 driver in the linux kernel.

Changes:

* kernel thread cleanups (exit/rmmod sync using completions, wait queue
  fixes).

* add support for more cards.

* improved infrared remote support.
parent 98e769bc
......@@ -834,7 +834,7 @@ struct saa7134_board saa7134_boards[] = {
}},
},
[SAA7134_BOARD_ECS_TVP3XP] = {
.name = "Elitegroup ECS TVP3XP FM1216 Tuner Card",
.name = "Elitegroup ECS TVP3XP FM1216 Tuner Card(PAL-BG,FM) ",
.audio_clock = 0x187de7, // xtal 32.1 MHz
.tuner_type = TUNER_PHILIPS_PAL,
.inputs = {{
......@@ -865,6 +865,82 @@ struct saa7134_board saa7134_boards[] = {
.amux = LINE2,
},
},
[SAA7134_BOARD_ECS_TVP3XP_4CB5] = {
.name = "Elitegroup ECS TVP3XP FM1236 Tuner Card (NTSC,FM)",
.audio_clock = 0x187de7,
.tuner_type = TUNER_PHILIPS_NTSC,
.inputs = {{
.name = name_tv,
.vmux = 1,
.amux = TV,
.tv = 1,
},{
.name = name_tv_mono,
.vmux = 1,
.amux = LINE2,
.tv = 1,
},{
.name = name_comp1,
.vmux = 3,
.amux = LINE1,
},{
.name = name_svideo,
.vmux = 8,
.amux = LINE1,
},{
.name = "CVid over SVid",
.vmux = 0,
.amux = LINE1,
}},
.radio = {
.name = name_radio,
.amux = LINE2,
},
},
[SAA7134_BOARD_AVACSSMARTTV] = {
/* Roman Pszonczenko <romka@kolos.math.uni.lodz.pl> */
.name = "AVACS SmartTV",
.audio_clock = 0x00187de7,
.tuner_type = TUNER_PHILIPS_PAL,
.inputs = {{
.name = name_tv,
.vmux = 1,
.amux = TV,
.tv = 1,
},{
.name = name_tv_mono,
.vmux = 1,
.amux = LINE2,
.tv = 1,
},{
.name = name_comp1,
.vmux = 0,
.amux = LINE2,
},{
.name = name_comp2,
.vmux = 3,
.amux = LINE2,
},{
.name = name_svideo,
.vmux = 8,
.amux = LINE2,
}},
.radio = {
.name = name_radio,
.amux = LINE2,
.gpio = 0x200000,
},
},
[SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER] = {
/* Michael Smith <msmith@cbnco.com> */
.name = "AVerMedia DVD EZMaker",
.audio_clock = 0x00187de7,
.tuner_type = TUNER_ABSENT,
.inputs = {{
.name = name_comp1,
.vmux = 3,
}},
},
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
......@@ -1023,6 +1099,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subvendor = 0x1461, /* Avermedia Technologies Inc */
.subdevice = 0x2115,
.driver_data = SAA7134_BOARD_MD2819,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7130,
.subvendor = 0x1461, /* Avermedia Technologies Inc */
.subdevice = 0x10ff,
.driver_data = SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER,
},{
/* TransGear 3000TV */
.vendor = PCI_VENDOR_ID_PHILIPS,
......@@ -1043,6 +1125,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subdevice = 0x4cb4,
.driver_data = SAA7134_BOARD_ECS_TVP3XP,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x1019,
.subdevice = 0x4cb5,
.driver_data = SAA7134_BOARD_ECS_TVP3XP_4CB5,
},{
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
......@@ -1149,6 +1237,8 @@ int saa7134_board_init(struct saa7134_dev *dev)
break;
case SAA7134_BOARD_CINERGY400:
case SAA7134_BOARD_CINERGY600:
case SAA7134_BOARD_ECS_TVP3XP:
case SAA7134_BOARD_ECS_TVP3XP_4CB5:
dev->has_remote = 1;
break;
}
......
/*
* handle saa7134 IR remotes via linux kernel input layer.
*
* 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
......@@ -13,9 +15,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <linux/module.h>
......@@ -101,6 +100,63 @@ static IR_KEYTAB_TYPE cinergy_codes[IR_KEYTAB_SIZE] = {
[ 0x23 ] = KEY_STOP,
};
/* Alfons Geser <a.geser@cox.net> */
static IR_KEYTAB_TYPE eztv_codes[IR_KEYTAB_SIZE] = {
[ 18 ] = KEY_POWER,
[ 1 ] = KEY_TV, // DVR
[ 21 ] = KEY_VIDEO, // DVD
[ 23 ] = KEY_AUDIO, // music
// DVR mode / DVD mode / music mode
[ 27 ] = KEY_MUTE, // mute
[ 2 ] = KEY_RESERVED, // MTS/SAP / audio /autoseek
[ 30 ] = KEY_RESERVED, // closed captioning / subtitle / seek
[ 22 ] = KEY_ZOOM, // full screen
[ 28 ] = KEY_RESERVED, // video source / eject /delall
[ 29 ] = KEY_RESERVED, // playback / angle /del
[ 47 ] = KEY_SEARCH, // scan / menu / playlist
[ 48 ] = KEY_RESERVED, // CH surfing / bookmark / memo
[ 49 ] = KEY_HELP, // help
[ 50 ] = KEY_RESERVED, // num/memo
[ 51 ] = KEY_ESC, // cancel
[ 12 ] = KEY_UP, // up
[ 16 ] = KEY_DOWN, // down
[ 8 ] = KEY_LEFT, // left
[ 4 ] = KEY_RIGHT, // right
[ 3 ] = KEY_ENTER, // select
[ 31 ] = KEY_REWIND, // rewind
[ 32 ] = KEY_PLAYPAUSE, // play/pause
[ 41 ] = KEY_FORWARD, // forward
[ 20 ] = KEY_RESERVED, // repeat
[ 43 ] = KEY_RECORD, // recording
[ 44 ] = KEY_STOP, // stop
[ 45 ] = KEY_PLAY, // play
[ 46 ] = KEY_RESERVED, // snapshot
[ 0 ] = KEY_KP0,
[ 5 ] = KEY_KP1,
[ 6 ] = KEY_KP2,
[ 7 ] = KEY_KP3,
[ 9 ] = KEY_KP4,
[ 10 ] = KEY_KP5,
[ 11 ] = KEY_KP6,
[ 13 ] = KEY_KP7,
[ 14 ] = KEY_KP8,
[ 15 ] = KEY_KP9,
[ 42 ] = KEY_VOLUMEUP,
[ 17 ] = KEY_VOLUMEDOWN,
[ 24 ] = KEY_CHANNELUP, // CH.tracking up
[ 25 ] = KEY_CHANNELDOWN, // CH.tracking down
[ 19 ] = KEY_KPENTER, // enter
[ 33 ] = KEY_KPDOT, // . (decimal dot)
};
/* ---------------------------------------------------------------------- */
static int build_key(struct saa7134_dev *dev)
......@@ -111,9 +167,15 @@ static int build_key(struct saa7134_dev *dev)
/* rising SAA7134_GPIO_GPRESCAN reads the status */
saa_clearb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
saa_setb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
data = ir_extract_bits(gpio, ir->mask_keycode);
if (ir->polling) {
if (ir->last_gpio == gpio)
return 0;
ir->last_gpio = gpio;
}
data = ir_extract_bits(gpio, ir->mask_keycode);
printk("%s: build_key gpio=0x%x mask=0x%x data=%d\n",
dev->name, gpio, ir->mask_keycode, data);
......@@ -130,7 +192,21 @@ static int build_key(struct saa7134_dev *dev)
void saa7134_input_irq(struct saa7134_dev *dev)
{
struct saa7134_ir *ir = dev->remote;
if (!ir->polling)
build_key(dev);
}
static void saa7134_input_timer(unsigned long data)
{
struct saa7134_dev *dev = (struct saa7134_dev*)data;
struct saa7134_ir *ir = dev->remote;
unsigned long timeout;
build_key(dev);
timeout = jiffies + (ir->polling * HZ / 1000);
mod_timer(&ir->timer, timeout);
}
int saa7134_input_init1(struct saa7134_dev *dev)
......@@ -140,6 +216,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
u32 mask_keycode = 0;
u32 mask_keydown = 0;
u32 mask_keyup = 0;
int polling = 0;
int ir_type = IR_TYPE_OTHER;
/* detect & configure */
......@@ -158,6 +235,13 @@ int saa7134_input_init1(struct saa7134_dev *dev)
mask_keycode = 0x00003f;
mask_keyup = 0x040000;
break;
case SAA7134_BOARD_ECS_TVP3XP:
case SAA7134_BOARD_ECS_TVP3XP_4CB5:
ir_codes = eztv_codes;
mask_keycode = 0x00017c;
mask_keyup = 0x000002;
polling = 50; // ms
break;
}
if (NULL == ir_codes) {
printk("%s: Oops: IR config error [card=%d]\n",
......@@ -174,6 +258,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
ir->mask_keycode = mask_keycode;
ir->mask_keydown = mask_keydown;
ir->mask_keyup = mask_keyup;
ir->polling = polling;
/* init input device */
snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)",
......@@ -196,6 +281,14 @@ int saa7134_input_init1(struct saa7134_dev *dev)
/* all done */
dev->remote = ir;
if (ir->polling) {
init_timer(&ir->timer);
ir->timer.function = saa7134_input_timer;
ir->timer.data = (unsigned long)dev;
ir->timer.expires = jiffies + HZ;
add_timer(&ir->timer);
}
input_register_device(&dev->remote->dev);
printk("%s: registered input device for IR\n",dev->name);
return 0;
......@@ -207,6 +300,8 @@ void saa7134_input_fini(struct saa7134_dev *dev)
return;
input_unregister_device(&dev->remote->dev);
if (dev->remote->polling)
del_timer_sync(&dev->remote->timer);
kfree(dev->remote);
dev->remote = NULL;
}
......
......@@ -45,6 +45,9 @@ static unsigned int audio_ddep = 0;
MODULE_PARM(audio_ddep,"i");
MODULE_PARM_DESC(audio_ddep,"audio ddep overwrite");
static int audio_clock_override = UNSET;
MODULE_PARM(audio_clock_override, "i");
static int audio_clock_tweak = 0;
MODULE_PARM(audio_clock_tweak, "i");
MODULE_PARM_DESC(audio_clock_tweak, "Audio clock tick fine tuning for cards with audio crystal that's slightly off (range [-1024 .. 1024])");
......@@ -140,6 +143,9 @@ static void tvaudio_init(struct saa7134_dev *dev)
{
int clock = saa7134_boards[dev->board].audio_clock;
if (UNSET != audio_clock_override)
clock = audio_clock_override;
/* init all audio registers */
saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x00);
if (need_resched())
......@@ -296,8 +302,13 @@ static int tvaudio_sleep(struct saa7134_dev *dev, int timeout)
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&dev->thread.wq, &wait);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(timeout);
if (dev->thread.scan1 == dev->thread.scan2 && !dev->thread.shutdown) {
set_current_state(TASK_INTERRUPTIBLE);
if (timeout < 0)
schedule();
else
schedule_timeout(timeout);
}
remove_wait_queue(&dev->thread.wq, &wait);
return dev->thread.scan1 != dev->thread.scan2;
}
......@@ -457,18 +468,11 @@ static int tvaudio_thread(void *data)
unsigned int i, audio;
int max1,max2,carrier,rx,mode,lastmode;
lock_kernel();
daemonize("%s", dev->name);
dev->thread.task = current;
unlock_kernel();
if (dev->thread.notify != NULL)
up(dev->thread.notify);
allow_signal(SIGTERM);
for (;;) {
if (dev->thread.exit || signal_pending(current))
goto done;
interruptible_sleep_on(&dev->thread.wq);
if (dev->thread.exit || signal_pending(current))
tvaudio_sleep(dev,-1);
if (dev->thread.shutdown || signal_pending(current))
goto done;
restart:
......@@ -571,7 +575,7 @@ static int tvaudio_thread(void *data)
for (;;) {
if (tvaudio_sleep(dev,5*HZ))
goto restart;
if (dev->thread.exit || signal_pending(current))
if (dev->thread.shutdown || signal_pending(current))
break;
if (UNSET == dev->thread.mode) {
rx = tvaudio_getstereo(dev,&tvaudio[i]);
......@@ -587,9 +591,7 @@ static int tvaudio_thread(void *data)
}
done:
dev->thread.task = NULL;
if(dev->thread.notify != NULL)
up(dev->thread.notify);
complete_and_exit(&dev->thread.exit, 0);
return 0;
}
......@@ -721,22 +723,16 @@ static int tvaudio_thread_ddep(void *data)
struct saa7134_dev *dev = data;
u32 value, norms;
lock_kernel();
daemonize("%s", dev->name);
dev->thread.task = current;
unlock_kernel();
if (dev->thread.notify != NULL)
up(dev->thread.notify);
allow_signal(SIGTERM);
/* unmute */
saa_dsp_writel(dev, 0x474 >> 2, 0x00);
saa_dsp_writel(dev, 0x450 >> 2, 0x00);
for (;;) {
if (dev->thread.exit || signal_pending(current))
goto done;
interruptible_sleep_on(&dev->thread.wq);
if (dev->thread.exit || signal_pending(current))
tvaudio_sleep(dev,-1);
if (dev->thread.shutdown || signal_pending(current))
goto done;
restart:
......@@ -808,9 +804,7 @@ static int tvaudio_thread_ddep(void *data)
}
done:
dev->thread.task = NULL;
if(dev->thread.notify != NULL)
up(dev->thread.notify);
complete_and_exit(&dev->thread.exit, 0);
return 0;
}
......@@ -893,7 +887,6 @@ int saa7134_tvaudio_init2(struct saa7134_dev *dev)
{
DECLARE_MUTEX_LOCKED(sem);
int (*my_thread)(void *data) = NULL;
int rc;
/* enable I2S audio output */
if (saa7134_boards[dev->board].i2s_rate) {
......@@ -915,17 +908,16 @@ int saa7134_tvaudio_init2(struct saa7134_dev *dev)
my_thread = tvaudio_thread_ddep;
break;
}
dev->thread.pid = -1;
if (my_thread) {
/* start tvaudio thread */
init_waitqueue_head(&dev->thread.wq);
dev->thread.notify = &sem;
rc = kernel_thread(my_thread,dev,0);
if (rc < 0)
init_completion(&dev->thread.exit);
dev->thread.pid = kernel_thread(my_thread,dev,0);
if (dev->thread.pid < 0)
printk(KERN_WARNING "%s: kernel_thread() failed\n",
dev->name);
else
down(&sem);
dev->thread.notify = NULL;
wake_up_interruptible(&dev->thread.wq);
}
......@@ -934,15 +926,11 @@ int saa7134_tvaudio_init2(struct saa7134_dev *dev)
int saa7134_tvaudio_fini(struct saa7134_dev *dev)
{
DECLARE_MUTEX_LOCKED(sem);
/* shutdown tvaudio thread */
if (dev->thread.task) {
dev->thread.notify = &sem;
dev->thread.exit = 1;
if (dev->thread.pid >= 0) {
dev->thread.shutdown = 1;
wake_up_interruptible(&dev->thread.wq);
down(&sem);
dev->thread.notify = NULL;
wait_for_completion(&dev->thread.exit);
}
saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, 0x00); /* LINE1 */
return 0;
......@@ -950,7 +938,7 @@ int saa7134_tvaudio_fini(struct saa7134_dev *dev)
int saa7134_tvaudio_do_scan(struct saa7134_dev *dev)
{
if (dev->thread.task) {
if (dev->thread.pid >= 0) {
dev->thread.mode = UNSET;
dev->thread.scan2++;
wake_up_interruptible(&dev->thread.wq);
......
......@@ -151,6 +151,9 @@ struct saa7134_format {
#define SAA7134_BOARD_MANLI_MTV001 28
#define SAA7134_BOARD_TG3000TV 29
#define SAA7134_BOARD_ECS_TVP3XP 30
#define SAA7134_BOARD_ECS_TVP3XP_4CB5 31
#define SAA7134_BOARD_AVACSSMARTTV 32
#define SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER 33
#define SAA7134_INPUT_MAX 8
......@@ -212,10 +215,10 @@ struct saa7134_pgtable {
/* tvaudio thread status */
struct saa7134_thread {
struct task_struct *task;
pid_t pid;
struct completion exit;
wait_queue_head_t wq;
struct semaphore *notify;
unsigned int exit;
unsigned int shutdown;
unsigned int scan1;
unsigned int scan2;
unsigned int mode;
......@@ -319,6 +322,9 @@ struct saa7134_ir {
u32 mask_keycode;
u32 mask_keydown;
u32 mask_keyup;
int polling;
u32 last_gpio;
struct timer_list timer;
};
/* global device status */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment