Commit ed455954 authored by Gerd Knorr's avatar Gerd Knorr Committed by Linus Torvalds

[PATCH] v4l: cx88 driver update

This is a update for the cx88 tv card driver.  Changes:

* finally make it build with gcc 2.95 ;)

* add new tv cards.

* plenty of fixes for the TV sound code.

* use v4l2 API for communication with tuner + tda9887

* misc other minor stuff.
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 940a9e4a
...@@ -99,6 +99,10 @@ struct cx88_board cx88_boards[] = { ...@@ -99,6 +99,10 @@ struct cx88_board cx88_boards[] = {
.input = {{ .input = {{
.type = CX88_VMUX_TELEVISION, .type = CX88_VMUX_TELEVISION,
.vmux = 0, .vmux = 0,
.gpio0 = 0x000003ff,
.gpio1 = 0x000000ff,
.gpio2 = 0x000000ff,
.gpio3 = 0x00000000,
},{ },{
.type = CX88_VMUX_COMPOSITE1, .type = CX88_VMUX_COMPOSITE1,
.vmux = 1, .vmux = 1,
...@@ -111,6 +115,7 @@ struct cx88_board cx88_boards[] = { ...@@ -111,6 +115,7 @@ struct cx88_board cx88_boards[] = {
[CX88_BOARD_WINFAST2000XP] = { [CX88_BOARD_WINFAST2000XP] = {
.name = "Leadtek Winfast 2000XP Expert", .name = "Leadtek Winfast 2000XP Expert",
.tuner_type = 44, .tuner_type = 44,
.needs_tda9887 = 1,
.input = {{ .input = {{
.type = CX88_VMUX_TELEVISION, .type = CX88_VMUX_TELEVISION,
.vmux = 0, .vmux = 0,
...@@ -149,22 +154,33 @@ struct cx88_board cx88_boards[] = { ...@@ -149,22 +154,33 @@ struct cx88_board cx88_boards[] = {
.vmux = 0, .vmux = 0,
}}, }},
}, },
[CX88_BOARD_MSI_TVANYWHERE] = { [CX88_BOARD_MSI_TVANYWHERE_MASTER] = {
//added gpio values thanks to Torsten Seeboth
//values for PAL from DScaler
.name = "MSI TV-@nywhere Master", .name = "MSI TV-@nywhere Master",
.tuner_type = 33, .tuner_type = 33,
.needs_tda9887 = 1,
.input = {{ .input = {{
.type = CX88_VMUX_TELEVISION, .type = CX88_VMUX_TELEVISION,
.vmux = 0, .vmux = 0,
.gpio0 = 0x000040bf,
.gpio1 = 0x000080c0,
.gpio2 = 0x0000ff40,
.gpio3 = 0x00000000,
},{ },{
.type = CX88_VMUX_COMPOSITE1, .type = CX88_VMUX_COMPOSITE1,
.vmux = 1, .vmux = 1,
},{ .gpio0 = 0x000040bf,
// temporarly for testing ... .gpio1 = 0x000080c0,
.type = CX88_VMUX_COMPOSITE2, .gpio2 = 0x0000ff40,
.vmux = 2, .gpio3 = 0x00000000,
},{ },{
.type = CX88_VMUX_SVIDEO, .type = CX88_VMUX_SVIDEO,
.vmux = 2, .vmux = 2,
.gpio0 = 0x000040bf,
.gpio1 = 0x000080c0,
.gpio2 = 0x0000ff40,
.gpio3 = 0x00000000,
}}, }},
.radio = { .radio = {
.type = CX88_RADIO, .type = CX88_RADIO,
...@@ -199,8 +215,97 @@ struct cx88_board cx88_boards[] = { ...@@ -199,8 +215,97 @@ struct cx88_board cx88_boards[] = {
.type = CX88_RADIO, .type = CX88_RADIO,
}, },
}, },
[CX88_BOARD_IODATA_GVVCP3PCI] = {
.name = "IODATA GV-VCP3/PCI",
.tuner_type = TUNER_ABSENT,
.needs_tda9887 = 0,
.input = {{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 0,
},{
.type = CX88_VMUX_COMPOSITE2,
.vmux = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
}},
},
[CX88_BOARD_PROLINK_PLAYTVPVR] = {
.name = "Prolink PlayTV PVR",
.tuner_type = 43,
.needs_tda9887 = 1,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0xff00,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0xff03,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0xff03,
}},
.radio = {
.type = CX88_RADIO,
.gpio0 = 0xff00,
},
},
[CX88_BOARD_ASUS_PVR_416] = {
.name = "ASUS PVR-416",
.tuner_type = 43,
.needs_tda9887 = 1,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0x0000fde6,
.gpio1 = 0x00000000, // possibly for mpeg data
.gpio2 = 0x000000e9,
.gpio3 = 0x00000000,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
.gpio1 = 0x00000000, // possibly for mpeg data
.gpio2 = 0x000000e9,
.gpio3 = 0x00000000,
}},
.radio = {
.type = CX88_RADIO,
.gpio0 = 0x0000fde2,
.gpio1 = 0x00000000,
.gpio2 = 0x000000e9,
.gpio3 = 0x00000000,
},
},
[CX88_BOARD_MSI_TVANYWHERE] = {
.name = "MSI TV-@nywhere",
.tuner_type = 33,
.needs_tda9887 = 1,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0x00000fbf,
.gpio1 = 0x000000c0,
.gpio2 = 0x0000fc08,
.gpio3 = 0x00000000,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x00000fbf,
.gpio1 = 0x000000c0,
.gpio2 = 0x0000fc68,
.gpio3 = 0x00000000,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x00000fbf,
.gpio1 = 0x000000c0,
.gpio2 = 0x0000fc68,
.gpio3 = 0x00000000,
}},
},
}; };
const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards); const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards);
...@@ -240,6 +345,10 @@ struct cx88_subid cx88_subids[] = { ...@@ -240,6 +345,10 @@ struct cx88_subid cx88_subids[] = {
.subvendor = 0x107d, .subvendor = 0x107d,
.subdevice = 0x6620, .subdevice = 0x6620,
.card = CX88_BOARD_WINFAST_DV2000, .card = CX88_BOARD_WINFAST_DV2000,
},{
.subvendor = 0x107d,
.subdevice = 0x663b,
.card = CX88_BOARD_LEADTEK_PVR2000,
},{ },{
.subvendor = 0x107d, .subvendor = 0x107d,
.subdevice = 0x663C, .subdevice = 0x663C,
...@@ -251,12 +360,19 @@ struct cx88_subid cx88_subids[] = { ...@@ -251,12 +360,19 @@ struct cx88_subid cx88_subids[] = {
},{ },{
.subvendor = 0x1462, .subvendor = 0x1462,
.subdevice = 0x8606, .subdevice = 0x8606,
.card = CX88_BOARD_MSI_TVANYWHERE, .card = CX88_BOARD_MSI_TVANYWHERE_MASTER,
} },{
.subvendor = 0x10fc,
.subdevice = 0xd003,
.card = CX88_BOARD_IODATA_GVVCP3PCI,
},{
.subvendor = 0x1043,
.subdevice = 0x4823, /* with mpeg encoder */
.card = CX88_BOARD_ASUS_PVR_416,
}
}; };
const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids);
/* ----------------------------------------------------------------------- */ /* ----------------------------------------------------------------------- */
/* some leadtek specific stuff */ /* some leadtek specific stuff */
...@@ -386,20 +502,22 @@ static struct { ...@@ -386,20 +502,22 @@ static struct {
[ 0x02 ] = { .id = TUNER_ABSENT, [ 0x02 ] = { .id = TUNER_ABSENT,
.name = "PAL_B" }, .name = "PAL_B" },
[ 0x03 ] = { .id = TUNER_ABSENT, [ 0x03 ] = { .id = TUNER_ABSENT,
.name = "BAL_I" }, .name = "PAL_I" },
[ 0x04 ] = { .id = TUNER_ABSENT, [ 0x04 ] = { .id = TUNER_ABSENT,
.name = "PAL_D" }, .name = "PAL_D" },
[ 0x05 ] = { .id = TUNER_ABSENT, [ 0x05 ] = { .id = TUNER_ABSENT,
.name = "SECAM" }, .name = "SECAM" },
[ 0x10 ] = { .id = TUNER_ABSENT, .fm = 1, [ 0x10 ] = { .id = TUNER_ABSENT,
.fm = 1,
.name = "TEMIC_4049" }, .name = "TEMIC_4049" },
[ 0x11 ] = { .id = TUNER_TEMIC_4136FY5, [ 0x11 ] = { .id = TUNER_TEMIC_4136FY5,
.name = "TEMIC_4136" }, .name = "TEMIC_4136" },
[ 0x12 ] = { .id = TUNER_ABSENT, [ 0x12 ] = { .id = TUNER_ABSENT,
.name = "TEMIC_4146" }, .name = "TEMIC_4146" },
[ 0x20 ] = { .id = TUNER_PHILIPS_FQ1216ME, .fm = 1, [ 0x20 ] = { .id = TUNER_PHILIPS_FQ1216ME,
.fm = 1,
.name = "PHILIPS_FQ1216_MK3" }, .name = "PHILIPS_FQ1216_MK3" },
[ 0x21 ] = { .id = TUNER_ABSENT, .fm = 1, [ 0x21 ] = { .id = TUNER_ABSENT, .fm = 1,
.name = "PHILIPS_FQ1236_MK3" }, .name = "PHILIPS_FQ1236_MK3" },
...@@ -454,7 +572,33 @@ i2c_eeprom(struct i2c_client *c, unsigned char *eedata, int len) ...@@ -454,7 +572,33 @@ i2c_eeprom(struct i2c_client *c, unsigned char *eedata, int len)
return 0; return 0;
} }
void __devinit cx88_card_setup(struct cx8800_dev *dev) void cx88_card_list(struct cx8800_dev *dev)
{
int i;
if (0 == dev->pci->subsystem_vendor &&
0 == dev->pci->subsystem_device) {
printk("%s: Your board has no valid PCI Subsystem ID and thus can't\n"
"%s: be autodetected. Please pass card=<n> insmod option to\n"
"%s: workaround that. Redirect complaints to the vendor of\n"
"%s: the TV card. Best regards,\n"
"%s: -- tux\n",
dev->name,dev->name,dev->name,dev->name,dev->name);
} else {
printk("%s: Your board isn't known (yet) to the driver. You can\n"
"%s: try to pick one of the existing card configs via\n"
"%s: card=<n> insmod option. Updating to the latest\n"
"%s: version might help as well.\n",
dev->name,dev->name,dev->name,dev->name);
}
printk("%s: Here is a list of valid choices for the card=<n> insmod option:\n",
dev->name);
for (i = 0; i < cx88_bcount; i++)
printk("%s: card=%d -> %s\n",
dev->name, i, cx88_boards[i].name);
}
void cx88_card_setup(struct cx8800_dev *dev)
{ {
static u8 eeprom[128]; static u8 eeprom[128];
...@@ -474,6 +618,9 @@ void __devinit cx88_card_setup(struct cx8800_dev *dev) ...@@ -474,6 +618,9 @@ void __devinit cx88_card_setup(struct cx8800_dev *dev)
i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom)); i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom));
leadtek_eeprom(dev,eeprom); leadtek_eeprom(dev,eeprom);
break; break;
case CX88_BOARD_ASUS_PVR_416:
dev->has_radio = 1;
break;
} }
} }
...@@ -483,6 +630,7 @@ EXPORT_SYMBOL(cx88_boards); ...@@ -483,6 +630,7 @@ EXPORT_SYMBOL(cx88_boards);
EXPORT_SYMBOL(cx88_bcount); EXPORT_SYMBOL(cx88_bcount);
EXPORT_SYMBOL(cx88_subids); EXPORT_SYMBOL(cx88_subids);
EXPORT_SYMBOL(cx88_idcount); EXPORT_SYMBOL(cx88_idcount);
EXPORT_SYMBOL(cx88_card_list);
EXPORT_SYMBOL(cx88_card_setup); EXPORT_SYMBOL(cx88_card_setup);
/* /*
......
...@@ -22,8 +22,6 @@ ...@@ -22,8 +22,6 @@
*/ */
#define __NO_VERSION__ 1
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h> #include <linux/init.h>
......
...@@ -599,10 +599,20 @@ ...@@ -599,10 +599,20 @@
#define EN_I2SIN_STR2DAC 0x00004000 #define EN_I2SIN_STR2DAC 0x00004000
#define EN_I2SIN_ENABLE 0x00008000 #define EN_I2SIN_ENABLE 0x00008000
#if 0
/* old */
#define EN_DMTRX_SUMDIFF 0x00000800 #define EN_DMTRX_SUMDIFF 0x00000800
#define EN_DMTRX_SUMR 0x00000880 #define EN_DMTRX_SUMR 0x00000880
#define EN_DMTRX_LR 0x00000900 #define EN_DMTRX_LR 0x00000900
#define EN_DMTRX_MONO 0x00000980 #define EN_DMTRX_MONO 0x00000980
#else
/* dscaler cvs */
#define EN_DMTRX_SUMDIFF (0 << 7)
#define EN_DMTRX_SUMR (1 << 7)
#define EN_DMTRX_LR (2 << 7)
#define EN_DMTRX_MONO (3 << 7)
#define EN_DMTRX_BYPASS (1 << 11)
#endif
// Video // Video
#define VID_CAPTURE_CONTROL 0x310180 #define VID_CAPTURE_CONTROL 0x310180
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/smp_lock.h>
#include "cx88.h" #include "cx88.h"
...@@ -60,6 +61,33 @@ MODULE_PARM_DESC(audio_debug,"enable debug messages [audio]"); ...@@ -60,6 +61,33 @@ MODULE_PARM_DESC(audio_debug,"enable debug messages [audio]");
/* ----------------------------------------------------------- */ /* ----------------------------------------------------------- */
static char *aud_ctl_names[64] =
{
[ EN_BTSC_FORCE_MONO ] = "BTSC_FORCE_MONO",
[ EN_BTSC_FORCE_STEREO ] = "BTSC_FORCE_STEREO",
[ EN_BTSC_FORCE_SAP ] = "BTSC_FORCE_SAP",
[ EN_BTSC_AUTO_STEREO ] = "BTSC_AUTO_STEREO",
[ EN_BTSC_AUTO_SAP ] = "BTSC_AUTO_SAP",
[ EN_A2_FORCE_MONO1 ] = "A2_FORCE_MONO1",
[ EN_A2_FORCE_MONO2 ] = "A2_FORCE_MONO2",
[ EN_A2_FORCE_STEREO ] = "A2_FORCE_STEREO",
[ EN_A2_AUTO_MONO2 ] = "A2_AUTO_MONO2",
[ EN_A2_AUTO_STEREO ] = "A2_AUTO_STEREO",
[ EN_EIAJ_FORCE_MONO1 ] = "EIAJ_FORCE_MONO1",
[ EN_EIAJ_FORCE_MONO2 ] = "EIAJ_FORCE_MONO2",
[ EN_EIAJ_FORCE_STEREO ] = "EIAJ_FORCE_STEREO",
[ EN_EIAJ_AUTO_MONO2 ] = "EIAJ_AUTO_MONO2",
[ EN_EIAJ_AUTO_STEREO ] = "EIAJ_AUTO_STEREO",
[ EN_NICAM_FORCE_MONO1 ] = "NICAM_FORCE_MONO1",
[ EN_NICAM_FORCE_MONO2 ] = "NICAM_FORCE_MONO2",
[ EN_NICAM_FORCE_STEREO ] = "NICAM_FORCE_STEREO",
[ EN_NICAM_AUTO_MONO2 ] = "NICAM_AUTO_MONO2",
[ EN_NICAM_AUTO_STEREO ] = "NICAM_AUTO_STEREO",
[ EN_FMRADIO_FORCE_MONO ] = "FMRADIO_FORCE_MONO",
[ EN_FMRADIO_FORCE_STEREO ] = "FMRADIO_FORCE_STEREO",
[ EN_FMRADIO_AUTO_STEREO ] = "FMRADIO_AUTO_STEREO",
};
struct rlist { struct rlist {
u32 reg; u32 reg;
u32 val; u32 val;
...@@ -125,26 +153,116 @@ static void set_audio_finish(struct cx8800_dev *dev) ...@@ -125,26 +153,116 @@ static void set_audio_finish(struct cx8800_dev *dev)
static void set_audio_standard_BTSC(struct cx8800_dev *dev, unsigned int sap) static void set_audio_standard_BTSC(struct cx8800_dev *dev, unsigned int sap)
{ {
static const struct rlist btsc[] = { static const struct rlist btsc[] = {
/* Magic stuff from leadtek driver + datasheet.*/ /* from dscaler */
{ AUD_DBX_IN_GAIN, 0x4734 }, { AUD_OUT1_SEL, 0x00000013 },
{ AUD_DBX_WBE_GAIN, 0x4640 }, { AUD_OUT1_SHIFT, 0x00000000 },
{ AUD_DBX_SE_GAIN, 0x8D31 }, { AUD_POLY0_DDS_CONSTANT, 0x0012010c },
{ AUD_DEEMPH0_G0, 0x1604 }, { AUD_DMD_RA_DDS, 0x00c3e7aa },
{ AUD_PHASE_FIX_CTL, 0x0020 }, { AUD_DBX_IN_GAIN, 0x00004734 },
{ AUD_DBX_WBE_GAIN, 0x00004640 },
{ AUD_DBX_SE_GAIN, 0x00008d31 },
{ AUD_DCOC_0_SRC, 0x0000001a },
{ AUD_IIR1_4_SEL, 0x00000021 },
{ AUD_DCOC_PASS_IN, 0x00000003 },
{ AUD_DCOC_0_SHIFT_IN0, 0x0000000a },
{ AUD_DCOC_0_SHIFT_IN1, 0x00000008 },
{ AUD_DCOC_1_SHIFT_IN0, 0x0000000a },
{ AUD_DCOC_1_SHIFT_IN1, 0x00000008 },
{ AUD_DN0_FREQ, 0x0000283b },
{ AUD_DN2_SRC_SEL, 0x00000008 },
{ AUD_DN2_FREQ, 0x00003000 },
{ AUD_DN2_AFC, 0x00000002 },
{ AUD_DN2_SHFT, 0x00000000 },
{ AUD_IIR2_2_SEL, 0x00000020 },
{ AUD_IIR2_2_SHIFT, 0x00000000 },
{ AUD_IIR2_3_SEL, 0x0000001f },
{ AUD_IIR2_3_SHIFT, 0x00000000 },
{ AUD_CRDC1_SRC_SEL, 0x000003ce },
{ AUD_CRDC1_SHIFT, 0x00000000 },
{ AUD_CORDIC_SHIFT_1, 0x00000007 },
{ AUD_DCOC_1_SRC, 0x0000001b },
{ AUD_DCOC1_SHIFT, 0x00000000 },
{ AUD_RDSI_SEL, 0x00000008 },
{ AUD_RDSQ_SEL, 0x00000008 },
{ AUD_RDSI_SHIFT, 0x00000000 },
{ AUD_RDSQ_SHIFT, 0x00000000 },
{ AUD_POLYPH80SCALEFAC, 0x00000003 },
{ /* end of list */ },
};
static const struct rlist btsc_sap[] = {
{ AUD_DBX_IN_GAIN, 0x00007200 },
{ AUD_DBX_WBE_GAIN, 0x00006200 },
{ AUD_DBX_SE_GAIN, 0x00006200 },
{ AUD_IIR1_1_SEL, 0x00000000 },
{ AUD_IIR1_3_SEL, 0x00000001 },
{ AUD_DN1_SRC_SEL, 0x00000007 },
{ AUD_IIR1_4_SHIFT, 0x00000006 },
{ AUD_IIR2_1_SHIFT, 0x00000000 },
{ AUD_IIR2_2_SHIFT, 0x00000000 },
{ AUD_IIR3_0_SHIFT, 0x00000000 },
{ AUD_IIR3_1_SHIFT, 0x00000000 },
{ AUD_IIR3_0_SEL, 0x0000000d },
{ AUD_IIR3_1_SEL, 0x0000000e },
{ AUD_DEEMPH1_SRC_SEL, 0x00000014 },
{ AUD_DEEMPH1_SHIFT, 0x00000000 },
{ AUD_DEEMPH1_G0, 0x00004000 },
{ AUD_DEEMPH1_A0, 0x00000000 },
{ AUD_DEEMPH1_B0, 0x00000000 },
{ AUD_DEEMPH1_A1, 0x00000000 },
{ AUD_DEEMPH1_B1, 0x00000000 },
{ AUD_OUT0_SEL, 0x0000003f },
{ AUD_OUT1_SEL, 0x0000003f },
{ AUD_DN1_AFC, 0x00000002 },
{ AUD_DCOC_0_SHIFT_IN0, 0x0000000a },
{ AUD_DCOC_0_SHIFT_IN1, 0x00000008 },
{ AUD_DCOC_1_SHIFT_IN0, 0x0000000a },
{ AUD_DCOC_1_SHIFT_IN1, 0x00000008 },
{ AUD_IIR1_0_SEL, 0x0000001d },
{ AUD_IIR1_2_SEL, 0x0000001e },
{ AUD_IIR2_1_SEL, 0x00000002 },
{ AUD_IIR2_2_SEL, 0x00000004 },
{ AUD_IIR3_2_SEL, 0x0000000f },
{ AUD_DCOC2_SHIFT, 0x00000001 },
{ AUD_IIR3_2_SHIFT, 0x00000001 },
{ AUD_DEEMPH0_SRC_SEL, 0x00000014 },
{ AUD_CORDIC_SHIFT_1, 0x00000006 },
{ AUD_POLY0_DDS_CONSTANT, 0x000e4db2 },
{ AUD_DMD_RA_DDS, 0x00f696e6 },
{ AUD_IIR2_3_SEL, 0x00000025 },
{ AUD_IIR1_4_SEL, 0x00000021 },
{ AUD_DN1_FREQ, 0x0000c965 },
{ AUD_DCOC_PASS_IN, 0x00000003 },
{ AUD_DCOC_0_SRC, 0x0000001a },
{ AUD_DCOC_1_SRC, 0x0000001b },
{ AUD_DCOC1_SHIFT, 0x00000000 },
{ AUD_RDSI_SEL, 0x00000009 },
{ AUD_RDSQ_SEL, 0x00000009 },
{ AUD_RDSI_SHIFT, 0x00000000 },
{ AUD_RDSQ_SHIFT, 0x00000000 },
{ AUD_POLYPH80SCALEFAC, 0x00000003 },
{ /* end of list */ }, { /* end of list */ },
}; };
dprintk("%s (status: unknown)\n",__FUNCTION__); // dscaler: exactly taken from driver,
set_audio_start(dev, 0x0001, // dscaler: don't know why to set EN_FMRADIO_EN_RDS
EN_BTSC_AUTO_STEREO); if (sap) {
set_audio_registers(dev, btsc); dprintk("%s SAP (status: unknown)\n",__FUNCTION__);
set_audio_start(dev, 0x0001,
EN_FMRADIO_EN_RDS | EN_BTSC_FORCE_SAP);
set_audio_registers(dev, btsc_sap);
} else {
dprintk("%s (status: known-good)\n",__FUNCTION__);
set_audio_start(dev, 0x0001,
EN_FMRADIO_EN_RDS | EN_BTSC_AUTO_STEREO);
set_audio_registers(dev, btsc);
}
set_audio_finish(dev); set_audio_finish(dev);
} }
static void set_audio_standard_NICAM(struct cx8800_dev *dev) static void set_audio_standard_NICAM(struct cx8800_dev *dev)
{ {
static const struct rlist nicam[] = { static const struct rlist nicam_common[] = {
/* from dscaler */
{ AUD_RATE_ADJ1, 0x00000010 }, { AUD_RATE_ADJ1, 0x00000010 },
{ AUD_RATE_ADJ2, 0x00000040 }, { AUD_RATE_ADJ2, 0x00000040 },
{ AUD_RATE_ADJ3, 0x00000100 }, { AUD_RATE_ADJ3, 0x00000100 },
...@@ -152,21 +270,64 @@ static void set_audio_standard_NICAM(struct cx8800_dev *dev) ...@@ -152,21 +270,64 @@ static void set_audio_standard_NICAM(struct cx8800_dev *dev)
{ AUD_RATE_ADJ5, 0x00001000 }, { AUD_RATE_ADJ5, 0x00001000 },
// { AUD_DMD_RA_DDS, 0x00c0d5ce }, // { AUD_DMD_RA_DDS, 0x00c0d5ce },
// Deemphasis 1:
{ AUD_DEEMPHGAIN_R, 0x000023c2 },
{ AUD_DEEMPHNUMER1_R, 0x0002a7bc },
{ AUD_DEEMPHNUMER2_R, 0x0003023e },
{ AUD_DEEMPHDENOM1_R, 0x0000f3d0 },
{ AUD_DEEMPHDENOM2_R, 0x00000000 },
#if 0
// Deemphasis 2: (other tv norm?)
{ AUD_DEEMPHGAIN_R, 0x0000c600 },
{ AUD_DEEMPHNUMER1_R, 0x00066738 },
{ AUD_DEEMPHNUMER2_R, 0x00066739 },
{ AUD_DEEMPHDENOM1_R, 0x0001e88c },
{ AUD_DEEMPHDENOM2_R, 0x0001e88c },
#endif
{ AUD_DEEMPHDENOM2_R, 0x00000000 },
{ AUD_ERRLOGPERIOD_R, 0x00000fff },
{ AUD_ERRINTRPTTHSHLD1_R, 0x000003ff },
{ AUD_ERRINTRPTTHSHLD2_R, 0x000000ff },
{ AUD_ERRINTRPTTHSHLD3_R, 0x0000003f },
{ AUD_POLYPH80SCALEFAC, 0x00000003 },
// setup QAM registers // setup QAM registers
{ AUD_PDF_DDS_CNST_BYTE2, 0x06 }, { AUD_PDF_DDS_CNST_BYTE2, 0x06 },
{ AUD_PDF_DDS_CNST_BYTE1, 0x82 }, { AUD_PDF_DDS_CNST_BYTE1, 0x82 },
{ AUD_PDF_DDS_CNST_BYTE0, 0x16 }, { AUD_PDF_DDS_CNST_BYTE0, 0x16 },
{ AUD_QAM_MODE, 0x05 }, { AUD_QAM_MODE, 0x05 },
{ /* end of list */ },
};
static const struct rlist nicam_pal_i[] = {
{ AUD_PDF_DDS_CNST_BYTE0, 0x12 },
{ AUD_PHACC_FREQ_8MSB, 0x3a },
{ AUD_PHACC_FREQ_8LSB, 0x93 },
{ /* end of list */ },
};
static const struct rlist nicam_default[] = {
{ AUD_PDF_DDS_CNST_BYTE0, 0x16 },
{ AUD_PHACC_FREQ_8MSB, 0x34 }, { AUD_PHACC_FREQ_8MSB, 0x34 },
{ AUD_PHACC_FREQ_8LSB, 0x4c }, { AUD_PHACC_FREQ_8LSB, 0x4c },
{ /* end of list */ }, { /* end of list */ },
}; };
dprintk("%s (status: unknown)\n",__FUNCTION__);
set_audio_start(dev, 0x0010, set_audio_start(dev, 0x0010,
EN_DMTRX_LR | EN_NICAM_FORCE_STEREO); EN_DMTRX_LR | EN_DMTRX_BYPASS | EN_NICAM_AUTO_STEREO);
set_audio_registers(dev, nicam); set_audio_registers(dev, nicam_common);
switch (dev->tvaudio) {
case WW_NICAM_I:
dprintk("%s PAL-I NICAM (status: unknown)\n",__FUNCTION__);
set_audio_registers(dev, nicam_pal_i);
case WW_NICAM_BGDKL:
dprintk("%s PAL NICAM (status: unknown)\n",__FUNCTION__);
set_audio_registers(dev, nicam_default);
break;
};
set_audio_finish(dev); set_audio_finish(dev);
} }
...@@ -297,7 +458,7 @@ static void set_audio_standard_NICAM_L(struct cx8800_dev *dev) ...@@ -297,7 +458,7 @@ static void set_audio_standard_NICAM_L(struct cx8800_dev *dev)
static void set_audio_standard_A2(struct cx8800_dev *dev) static void set_audio_standard_A2(struct cx8800_dev *dev)
{ {
/* from dscaler cvs */ /* from dscaler cvs */
static const struct rlist a2[] = { static const struct rlist a2_common[] = {
{ AUD_PDF_DDS_CNST_BYTE2, 0x06 }, { AUD_PDF_DDS_CNST_BYTE2, 0x06 },
{ AUD_PDF_DDS_CNST_BYTE1, 0x82 }, { AUD_PDF_DDS_CNST_BYTE1, 0x82 },
{ AUD_PDF_DDS_CNST_BYTE0, 0x12 }, { AUD_PDF_DDS_CNST_BYTE0, 0x12 },
...@@ -347,16 +508,20 @@ static void set_audio_standard_A2(struct cx8800_dev *dev) ...@@ -347,16 +508,20 @@ static void set_audio_standard_A2(struct cx8800_dev *dev)
{ AUD_RDSQ_SHIFT, 0x00000000 }, { AUD_RDSQ_SHIFT, 0x00000000 },
{ AUD_POLYPH80SCALEFAC, 0x00000001 }, { AUD_POLYPH80SCALEFAC, 0x00000001 },
// Table 1 { /* end of list */ },
};
static const struct rlist a2_table1[] = {
// PAL-BG
{ AUD_DMD_RA_DDS, 0x002a73bd }, { AUD_DMD_RA_DDS, 0x002a73bd },
{ AUD_C1_UP_THR, 0x00007000 }, { AUD_C1_UP_THR, 0x00007000 },
{ AUD_C1_LO_THR, 0x00005400 }, { AUD_C1_LO_THR, 0x00005400 },
{ AUD_C2_UP_THR, 0x00005400 }, { AUD_C2_UP_THR, 0x00005400 },
{ AUD_C2_LO_THR, 0x00003000 }, { AUD_C2_LO_THR, 0x00003000 },
{ /* end of list */ },
#if 0 };
// found this in WDM-driver for A2, must country spec. static const struct rlist a2_table2[] = {
// Table 2 // PAL-DK
{ AUD_DMD_RA_DDS, 0x002a73bd }, { AUD_DMD_RA_DDS, 0x002a73bd },
{ AUD_C1_UP_THR, 0x00007000 }, { AUD_C1_UP_THR, 0x00007000 },
{ AUD_C1_LO_THR, 0x00005400 }, { AUD_C1_LO_THR, 0x00005400 },
...@@ -364,8 +529,10 @@ static void set_audio_standard_A2(struct cx8800_dev *dev) ...@@ -364,8 +529,10 @@ static void set_audio_standard_A2(struct cx8800_dev *dev)
{ AUD_C2_LO_THR, 0x00003000 }, { AUD_C2_LO_THR, 0x00003000 },
{ AUD_DN0_FREQ, 0x00003a1c }, { AUD_DN0_FREQ, 0x00003a1c },
{ AUD_DN2_FREQ, 0x0000d2e0 }, { AUD_DN2_FREQ, 0x0000d2e0 },
{ /* end of list */ },
// Table 3 };
static const struct rlist a2_table3[] = {
// unknown, probably NTSC-M
{ AUD_DMD_RA_DDS, 0x002a2873 }, { AUD_DMD_RA_DDS, 0x002a2873 },
{ AUD_C1_UP_THR, 0x00003c00 }, { AUD_C1_UP_THR, 0x00003c00 },
{ AUD_C1_LO_THR, 0x00003000 }, { AUD_C1_LO_THR, 0x00003000 },
...@@ -375,140 +542,26 @@ static void set_audio_standard_A2(struct cx8800_dev *dev) ...@@ -375,140 +542,26 @@ static void set_audio_standard_A2(struct cx8800_dev *dev)
{ AUD_DN1_FREQ, 0x00003418 }, { AUD_DN1_FREQ, 0x00003418 },
{ AUD_DN2_FREQ, 0x000029c7 }, { AUD_DN2_FREQ, 0x000029c7 },
{ AUD_POLY0_DDS_CONSTANT, 0x000a7540 }, { AUD_POLY0_DDS_CONSTANT, 0x000a7540 },
#endif
{ /* end of list */ }, { /* end of list */ },
}; };
static const struct rlist a2_old[] = { set_audio_start(dev, 0x0004, EN_DMTRX_SUMDIFF | EN_A2_AUTO_STEREO);
{ AUD_DN0_FREQ, 0x0000312b }, set_audio_registers(dev, a2_common);
{ AUD_POLY0_DDS_CONSTANT, 0x000a62b4 }, switch (dev->tvaudio) {
{ AUD_IIR1_0_SEL, 0x00000000 }, case WW_A2_BG:
{ AUD_IIR1_1_SEL, 0x00000001 }, dprintk("%s PAL-BG A2 (status: known-good)\n",__FUNCTION__);
{ AUD_IIR1_2_SEL, 0x0000001f }, set_audio_registers(dev, a2_table1);
{ AUD_IIR1_3_SEL, 0x00000020 }, break;
{ AUD_IIR1_4_SEL, 0x00000023 }, case WW_A2_DK:
{ AUD_IIR1_5_SEL, 0x00000007 }, dprintk("%s PAL-DK A2 (status: known-good)\n",__FUNCTION__);
{ AUD_IIR1_0_SHIFT, 0x00000000 }, set_audio_registers(dev, a2_table2);
{ AUD_IIR1_1_SHIFT, 0x00000000 }, break;
{ AUD_IIR1_2_SHIFT, 0x00000007 }, case WW_A2_M:
{ AUD_IIR1_3_SHIFT, 0x00000007 }, dprintk("%s NTSC-M A2 (status: unknown)\n",__FUNCTION__);
{ AUD_IIR1_4_SHIFT, 0x00000007 }, set_audio_registers(dev, a2_table3);
{ AUD_IIR1_5_SHIFT, 0x00000000 }, break;
{ AUD_IIR2_0_SEL, 0x00000002 },
{ AUD_IIR2_1_SEL, 0x00000003 },
{ AUD_IIR2_2_SEL, 0x00000004 },
{ AUD_IIR2_3_SEL, 0x00000005 },
{ AUD_IIR3_0_SEL, 0x00000021 },
{ AUD_IIR3_1_SEL, 0x00000023 },
{ AUD_IIR3_2_SEL, 0x00000016 },
{ AUD_IIR3_0_SHIFT, 0x00000000 },
{ AUD_IIR3_1_SHIFT, 0x00000000 },
{ AUD_IIR3_2_SHIFT, 0x00000000 },
{ AUD_IIR4_0_SEL, 0x0000001d },
{ AUD_IIR4_1_SEL, 0x00000019 },
{ AUD_IIR4_2_SEL, 0x00000008 },
{ AUD_IIR4_0_SHIFT, 0x00000000 },
{ AUD_IIR4_1_SHIFT, 0x00000000 },
{ AUD_IIR4_2_SHIFT, 0x00000001 },
{ AUD_IIR4_0_CA0, 0x0003e57e },
{ AUD_IIR4_0_CA1, 0x00005e11 },
{ AUD_IIR4_0_CA2, 0x0003a7cf },
{ AUD_IIR4_0_CB0, 0x00002368 },
{ AUD_IIR4_0_CB1, 0x0003bf1b },
{ AUD_IIR4_1_CA0, 0x00006349 },
{ AUD_IIR4_1_CA1, 0x00006f27 },
{ AUD_IIR4_1_CA2, 0x0000e7a3 },
{ AUD_IIR4_1_CB0, 0x00005653 },
{ AUD_IIR4_1_CB1, 0x0000cf97 },
{ AUD_IIR4_2_CA0, 0x00006349 },
{ AUD_IIR4_2_CA1, 0x00006f27 },
{ AUD_IIR4_2_CA2, 0x0000e7a3 },
{ AUD_IIR4_2_CB0, 0x00005653 },
{ AUD_IIR4_2_CB1, 0x0000cf97 },
{ AUD_HP_MD_IIR4_1, 0x00000001 },
{ AUD_HP_PROG_IIR4_1, 0x00000017 },
{ AUD_DN1_FREQ, 0x00003618 },
{ AUD_DN1_SRC_SEL, 0x00000017 },
{ AUD_DN1_SHFT, 0x00000007 },
{ AUD_DN1_AFC, 0x00000000 },
{ AUD_DN1_FREQ_SHIFT, 0x00000000 },
{ AUD_DN2_SRC_SEL, 0x00000040 },
{ AUD_DN2_SHFT, 0x00000000 },
{ AUD_DN2_AFC, 0x00000002 },
{ AUD_DN2_FREQ, 0x0000caaf },
{ AUD_DN2_FREQ_SHIFT, 0x00000000 },
{ AUD_PDET_SRC, 0x00000014 },
{ AUD_PDET_SHIFT, 0x00000000 },
{ AUD_DEEMPH0_SRC_SEL, 0x00000011 },
{ AUD_DEEMPH1_SRC_SEL, 0x00000013 },
{ AUD_DEEMPH0_SHIFT, 0x00000000 },
{ AUD_DEEMPH1_SHIFT, 0x00000000 },
{ AUD_DEEMPH0_G0, 0x000004da },
{ AUD_DEEMPH0_A0, 0x0000777a },
{ AUD_DEEMPH0_B0, 0x00000000 },
{ AUD_DEEMPH0_A1, 0x0003f062 },
{ AUD_DEEMPH0_B1, 0x00000000 },
{ AUD_DEEMPH1_G0, 0x000004da },
{ AUD_DEEMPH1_A0, 0x0000777a },
{ AUD_DEEMPH1_B0, 0x00000000 },
{ AUD_DEEMPH1_A1, 0x0003f062 },
{ AUD_DEEMPH1_B1, 0x00000000 },
{ AUD_PLL_EN, 0x00000000 },
{ AUD_DMD_RA_DDS, 0x002a4efb },
{ AUD_RATE_ADJ1, 0x00001000 },
{ AUD_RATE_ADJ2, 0x00002000 },
{ AUD_RATE_ADJ3, 0x00003000 },
{ AUD_RATE_ADJ4, 0x00004000 },
{ AUD_RATE_ADJ5, 0x00005000 },
{ AUD_C2_UP_THR, 0x0000ffff },
{ AUD_C2_LO_THR, 0x0000e800 },
{ AUD_C1_UP_THR, 0x00008c00 },
{ AUD_C1_LO_THR, 0x00006c00 },
// ; Completely ditch AFC feedback
{ AUD_DCOC_0_SRC, 0x00000021 },
{ AUD_DCOC_1_SRC, 0x0000001a },
{ AUD_DCOC1_SHIFT, 0x00000000 },
{ AUD_DCOC_1_SHIFT_IN0, 0x0000000a },
{ AUD_DCOC_1_SHIFT_IN1, 0x00000008 },
{ AUD_DCOC_PASS_IN, 0x00000000 },
{ AUD_IIR4_0_SEL, 0x00000023 },
// ; Completely ditc FM-2 AFC feedback
{ AUD_DN1_AFC, 0x00000000 },
{ AUD_DCOC_2_SRC, 0x0000001b },
{ AUD_IIR4_1_SEL, 0x00000025 },
// ; WARNING!!! THIS CHANGE WAS NOT EXPECTED!!!
// ; Swap I & Q inputs into second rotator
// ; to reverse frequency and therefor invert
// ; phase from the cordic FM demodulator
// ; (frequency rotation must also be reversed
{ AUD_DN2_SRC_SEL, 0x00000001 },
{ AUD_DN2_FREQ, 0x00003551 },
// setup Audio PLL
{ AUD_PLL_PRESCALE, 0x00000002 },
{ AUD_PLL_INT, 0x0000001f },
{ /* end of list */ },
}; };
set_audio_finish(dev);
dprintk("%s (status: WorksForMe[tm])\n",__FUNCTION__);
if (0) {
/* old code */
set_audio_start(dev, 0x0004, EN_DMTRX_SUMR | EN_A2_AUTO_STEREO);
set_audio_registers(dev, a2_old);
set_audio_finish(dev);
} else {
/* new code */
set_audio_start(dev, 0x0004, EN_DMTRX_LR | EN_A2_AUTO_STEREO);
set_audio_registers(dev, a2);
set_audio_finish(dev);
}
} }
static void set_audio_standard_EIAJ(struct cx8800_dev *dev) static void set_audio_standard_EIAJ(struct cx8800_dev *dev)
...@@ -617,9 +670,9 @@ void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t) ...@@ -617,9 +670,9 @@ void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t)
reg = cx_read(AUD_STATUS); reg = cx_read(AUD_STATUS);
mode = reg & 0x03; mode = reg & 0x03;
pilot = (reg >> 2) & 0x03; pilot = (reg >> 2) & 0x03;
dprintk("AUD_STATUS: %s / %s [status=0x%x,ctl=0x%x,vol=0x%x]\n", dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n",
m[mode], p[pilot], reg, reg, m[mode], p[pilot],
cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL)); aud_ctl_names[cx_read(AUD_CTL) & 63]);
t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP | t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP |
V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
...@@ -628,6 +681,8 @@ void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t) ...@@ -628,6 +681,8 @@ void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t)
switch (dev->tvaudio) { switch (dev->tvaudio) {
case WW_A2_BG: case WW_A2_BG:
case WW_A2_DK:
case WW_A2_M:
if (1 == pilot) { if (1 == pilot) {
/* stereo */ /* stereo */
t->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; t->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
...@@ -659,6 +714,8 @@ void cx88_set_stereo(struct cx8800_dev *dev, u32 mode) ...@@ -659,6 +714,8 @@ void cx88_set_stereo(struct cx8800_dev *dev, u32 mode)
switch (dev->tvaudio) { switch (dev->tvaudio) {
case WW_A2_BG: case WW_A2_BG:
case WW_A2_DK:
case WW_A2_M:
switch (mode) { switch (mode) {
case V4L2_TUNER_MODE_MONO: case V4L2_TUNER_MODE_MONO:
case V4L2_TUNER_MODE_LANG1: case V4L2_TUNER_MODE_LANG1:
...@@ -717,6 +774,32 @@ void cx88_set_stereo(struct cx8800_dev *dev, u32 mode) ...@@ -717,6 +774,32 @@ void cx88_set_stereo(struct cx8800_dev *dev, u32 mode)
return; return;
} }
/* just monitor the audio status for now ... */
int cx88_audio_thread(void *data)
{
struct cx8800_dev *dev = data;
struct v4l2_tuner t;
daemonize("msp3400");
allow_signal(SIGTERM);
dprintk("cx88: tvaudio thread started\n");
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ*3);
if (signal_pending(current))
break;
if (dev->shutdown)
break;
memset(&t,0,sizeof(t));
cx88_get_stereo(dev,&t);
}
dprintk("cx88: tvaudio thread exiting\n");
complete_and_exit(&dev->texit, 0);
}
/* /*
* Local variables: * Local variables:
* c-basic-offset: 8 * c-basic-offset: 8
......
...@@ -28,17 +28,14 @@ void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f) ...@@ -28,17 +28,14 @@ void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f)
f->fmt.vbi.count[0] = VBI_LINE_COUNT; f->fmt.vbi.count[0] = VBI_LINE_COUNT;
f->fmt.vbi.count[1] = VBI_LINE_COUNT; f->fmt.vbi.count[1] = VBI_LINE_COUNT;
switch (dev->tvnorm->id) { if (dev->tvnorm->id & V4L2_STD_525_60) {
case V4L2_STD_NTSC_M: /* ntsc */
case V4L2_STD_NTSC_M_JP:
f->fmt.vbi.sampling_rate = 28636363; f->fmt.vbi.sampling_rate = 28636363;
f->fmt.vbi.start[0] = 10 -1; f->fmt.vbi.start[0] = 10 -1;
f->fmt.vbi.start[1] = 273 -1; f->fmt.vbi.start[1] = 273 -1;
break;
case V4L2_STD_PAL_BG: } else if (V4L2_STD_625_50) {
case V4L2_STD_PAL_DK: /* pal */
case V4L2_STD_PAL_I:
case V4L2_STD_SECAM:
f->fmt.vbi.sampling_rate = 35468950; f->fmt.vbi.sampling_rate = 35468950;
f->fmt.vbi.start[0] = 7 -1; f->fmt.vbi.start[0] = 7 -1;
f->fmt.vbi.start[1] = 319 -1; f->fmt.vbi.start[1] = 319 -1;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* device driver for Conexant 2388x based TV cards * device driver for Conexant 2388x based TV cards
* video4linux video interface * video4linux video interface
* *
* (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
...@@ -19,8 +19,6 @@ ...@@ -19,8 +19,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/ */
#define __NO_VERSION__ 1
#include <linux/init.h> #include <linux/init.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -32,6 +30,8 @@ ...@@ -32,6 +30,8 @@
#include "cx88.h" #include "cx88.h"
#define V4L2_I2C_CLIENTS 1
MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -194,8 +194,13 @@ static struct cx8800_tvnorm tvnorms[] = { ...@@ -194,8 +194,13 @@ static struct cx8800_tvnorm tvnorms[] = {
.cxiformat = VideoFormatPAL60, .cxiformat = VideoFormatPAL60,
.cxoformat = 0x181f0008, .cxoformat = 0x181f0008,
},{ },{
.name = "SECAM", .name = "SECAM-L",
.id = V4L2_STD_SECAM, .id = V4L2_STD_SECAM_L,
.cxiformat = VideoFormatSECAM,
.cxoformat = 0x181f0008,
},{
.name = "SECAM-DK",
.id = V4L2_STD_SECAM_DK,
.cxiformat = VideoFormatSECAM, .cxiformat = VideoFormatSECAM,
.cxoformat = 0x181f0008, .cxoformat = 0x181f0008,
} }
...@@ -483,35 +488,38 @@ static int set_tvaudio(struct cx8800_dev *dev) ...@@ -483,35 +488,38 @@ static int set_tvaudio(struct cx8800_dev *dev)
if (CX88_VMUX_TELEVISION != INPUT(dev->input)->type) if (CX88_VMUX_TELEVISION != INPUT(dev->input)->type)
return 0; return 0;
switch (dev->tvnorm->id) { if (V4L2_STD_PAL_BG & dev->tvnorm->id) {
case V4L2_STD_PAL_BG:
dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_BG; dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_BG;
break;
case V4L2_STD_PAL_DK: } else if (V4L2_STD_PAL_DK & dev->tvnorm->id) {
dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_DK; dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_DK;
break;
case V4L2_STD_PAL_I: } else if (V4L2_STD_PAL_I & dev->tvnorm->id) {
dev->tvaudio = WW_NICAM_I; dev->tvaudio = WW_NICAM_I;
break;
case V4L2_STD_SECAM: } else if (V4L2_STD_SECAM_L & dev->tvnorm->id) {
dev->tvaudio = WW_SYSTEM_L_AM; /* FIXME: fr != ru */ dev->tvaudio = WW_SYSTEM_L_AM;
break;
case V4L2_STD_NTSC_M: } else if (V4L2_STD_SECAM_DK & dev->tvnorm->id) {
dev->tvaudio = WW_A2_DK;
} else if ((V4L2_STD_NTSC_M & dev->tvnorm->id) ||
(V4L2_STD_PAL_M & dev->tvnorm->id)) {
dev->tvaudio = WW_BTSC; dev->tvaudio = WW_BTSC;
break;
case V4L2_STD_NTSC_M_JP: } else if (V4L2_STD_NTSC_M_JP & dev->tvnorm->id) {
dev->tvaudio = WW_EIAJ; dev->tvaudio = WW_EIAJ;
break;
default: } else {
dprintk(1,"tvaudio support needs work for this tv norm [%s], sorry\n", printk("%s: tvaudio support needs work for this tv norm [%s], sorry\n",
dev->tvnorm->name); dev->name, dev->tvnorm->name);
dev->tvaudio = 0; dev->tvaudio = 0;
return 0; return 0;
} }
cx_andor(MO_AFECFG_IO, 0x1f, 0x0); cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
cx88_set_tvaudio(dev); cx88_set_tvaudio(dev);
cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); // cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO);
cx_write(MO_AUDD_LNGTH, 128/8); /* fifo size */ cx_write(MO_AUDD_LNGTH, 128/8); /* fifo size */
cx_write(MO_AUDR_LNGTH, 128/8); /* fifo size */ cx_write(MO_AUDR_LNGTH, 128/8); /* fifo size */
...@@ -526,7 +534,6 @@ static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm) ...@@ -526,7 +534,6 @@ static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm)
u32 vdec_clock; u32 vdec_clock;
u64 tmp64; u64 tmp64;
u32 bdelay,agcdelay,htotal; u32 bdelay,agcdelay,htotal;
struct video_channel c;
dev->tvnorm = norm; dev->tvnorm = norm;
fsc8 = norm_fsc8(norm); fsc8 = norm_fsc8(norm);
...@@ -592,30 +599,43 @@ static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm) ...@@ -592,30 +599,43 @@ static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm)
set_tvaudio(dev); set_tvaudio(dev);
// tell i2c chips // tell i2c chips
memset(&c,0,sizeof(c)); #ifdef V4L2_I2C_CLIENTS
c.channel = dev->input; cx8800_call_i2c_clients(dev,VIDIOC_S_STD,&norm->id);
c.norm = VIDEO_MODE_PAL; #else
if ((norm->id & (V4L2_STD_NTSC_M|V4L2_STD_NTSC_M_JP))) {
c.norm = VIDEO_MODE_NTSC; struct video_channel c;
if (norm->id & V4L2_STD_SECAM) memset(&c,0,sizeof(c));
c.norm = VIDEO_MODE_SECAM; c.channel = dev->input;
cx8800_call_i2c_clients(dev,VIDIOCSCHAN,&c); c.norm = VIDEO_MODE_PAL;
if ((norm->id & (V4L2_STD_NTSC_M|V4L2_STD_NTSC_M_JP)))
c.norm = VIDEO_MODE_NTSC;
if (norm->id & V4L2_STD_SECAM)
c.norm = VIDEO_MODE_SECAM;
cx8800_call_i2c_clients(dev,VIDIOCSCHAN,&c);
}
#endif
// done // done
return 0; return 0;
} }
static int set_scale(struct cx8800_dev *dev, unsigned int width, unsigned int height, static int set_scale(struct cx8800_dev *dev, unsigned int width, unsigned int height,
int interlaced) enum v4l2_field field)
{ {
unsigned int swidth = norm_swidth(dev->tvnorm); unsigned int swidth = norm_swidth(dev->tvnorm);
unsigned int sheight = norm_maxh(dev->tvnorm); unsigned int sheight = norm_maxh(dev->tvnorm);
u32 value; u32 value;
dprintk(1,"set_scale: %dx%d [%s]\n", width, height, dev->tvnorm->name); dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
V4L2_FIELD_HAS_TOP(field) ? "T" : "",
V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
dev->tvnorm->name);
if (!V4L2_FIELD_HAS_BOTH(field))
height *= 2;
// recalc H delay and scale registers // recalc H delay and scale registers
value = (width * norm_hdelay(dev->tvnorm)) / swidth; value = (width * norm_hdelay(dev->tvnorm)) / swidth;
value &= 0x3fe;
cx_write(MO_HDELAY_EVEN, value); cx_write(MO_HDELAY_EVEN, value);
cx_write(MO_HDELAY_ODD, value); cx_write(MO_HDELAY_ODD, value);
dprintk(1,"set_scale: hdelay 0x%04x\n", value); dprintk(1,"set_scale: hdelay 0x%04x\n", value);
...@@ -646,7 +666,7 @@ static int set_scale(struct cx8800_dev *dev, unsigned int width, unsigned int he ...@@ -646,7 +666,7 @@ static int set_scale(struct cx8800_dev *dev, unsigned int width, unsigned int he
// setup filters // setup filters
value = 0; value = 0;
value |= (1 << 19); // CFILT (default) value |= (1 << 19); // CFILT (default)
if (interlaced) if (V4L2_FIELD_INTERLACED == field)
value |= (1 << 3); // VINT (interlaced vertical scaling) value |= (1 << 3); // VINT (interlaced vertical scaling)
if (width < 385) if (width < 385)
value |= (1 << 0); // 3-tap interpolation value |= (1 << 0); // 3-tap interpolation
...@@ -675,10 +695,12 @@ static int video_mux(struct cx8800_dev *dev, unsigned int input) ...@@ -675,10 +695,12 @@ static int video_mux(struct cx8800_dev *dev, unsigned int input)
switch (INPUT(input)->type) { switch (INPUT(input)->type) {
case CX88_VMUX_SVIDEO: case CX88_VMUX_SVIDEO:
cx_andor(MO_AFECFG_IO, 0x01, 0x01); cx_set(MO_AFECFG_IO, 0x00000001);
cx_set(MO_INPUT_FORMAT, 0x00010010);
break; break;
default: default:
cx_andor(MO_AFECFG_IO, 0x01, 0x00); cx_clear(MO_AFECFG_IO, 0x00000001);
cx_clear(MO_INPUT_FORMAT, 0x00010010);
break; break;
} }
return 0; return 0;
...@@ -693,7 +715,7 @@ static int start_video_dma(struct cx8800_dev *dev, ...@@ -693,7 +715,7 @@ static int start_video_dma(struct cx8800_dev *dev,
/* setup fifo + format */ /* setup fifo + format */
cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH21], cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH21],
buf->bpl, buf->risc.dma); buf->bpl, buf->risc.dma);
set_scale(dev, buf->vb.width, buf->vb.height, 1); set_scale(dev, buf->vb.width, buf->vb.height, buf->vb.field);
cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma); cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma);
/* reset counter */ /* reset counter */
...@@ -1350,6 +1372,9 @@ static int get_control(struct cx8800_dev *dev, struct v4l2_control *ctl) ...@@ -1350,6 +1372,9 @@ static int get_control(struct cx8800_dev *dev, struct v4l2_control *ctl)
case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BALANCE:
ctl->value = (value & 0x40) ? (value & 0x3f) : (0x40 - (value & 0x3f)); ctl->value = (value & 0x40) ? (value & 0x3f) : (0x40 - (value & 0x3f));
break; break;
case V4L2_CID_AUDIO_VOLUME:
ctl->value = 0x3f - (value & 0x3f);
break;
default: default:
ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift; ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift;
break; break;
...@@ -1378,6 +1403,9 @@ static int set_control(struct cx8800_dev *dev, struct v4l2_control *ctl) ...@@ -1378,6 +1403,9 @@ static int set_control(struct cx8800_dev *dev, struct v4l2_control *ctl)
case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BALANCE:
value = (ctl->value < 0x40) ? (0x40 - ctl->value) : ctl->value; value = (ctl->value < 0x40) ? (0x40 - ctl->value) : ctl->value;
break; break;
case V4L2_CID_AUDIO_VOLUME:
value = 0x3f - (ctl->value & 0x3f);
break;
case V4L2_CID_SATURATION: case V4L2_CID_SATURATION:
/* special v_sat handling */ /* special v_sat handling */
v_sat_value = ctl->value - (0x7f - 0x5a); v_sat_value = ctl->value - (0x7f - 0x5a);
...@@ -1409,7 +1437,7 @@ static void init_controls(struct cx8800_dev *dev) ...@@ -1409,7 +1437,7 @@ static void init_controls(struct cx8800_dev *dev)
}; };
static struct v4l2_control volume = { static struct v4l2_control volume = {
.id = V4L2_CID_AUDIO_VOLUME, .id = V4L2_CID_AUDIO_VOLUME,
.value = 0, .value = 0x3f,
}; };
set_control(dev,&mute); set_control(dev,&mute);
...@@ -1459,15 +1487,12 @@ static int cx8800_try_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, ...@@ -1459,15 +1487,12 @@ static int cx8800_try_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh,
maxw = norm_maxw(dev->tvnorm); maxw = norm_maxw(dev->tvnorm);
maxh = norm_maxh(dev->tvnorm); maxh = norm_maxh(dev->tvnorm);
#if 0
if (V4L2_FIELD_ANY == field) { if (V4L2_FIELD_ANY == field) {
field = (f->fmt.pix.height > maxh/2) field = (f->fmt.pix.height > maxh/2)
? V4L2_FIELD_INTERLACED ? V4L2_FIELD_INTERLACED
: V4L2_FIELD_BOTTOM; : V4L2_FIELD_BOTTOM;
} }
#else
field = V4L2_FIELD_INTERLACED;
#endif
switch (field) { switch (field) {
case V4L2_FIELD_TOP: case V4L2_FIELD_TOP:
case V4L2_FIELD_BOTTOM: case V4L2_FIELD_BOTTOM:
...@@ -1480,14 +1505,15 @@ static int cx8800_try_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, ...@@ -1480,14 +1505,15 @@ static int cx8800_try_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh,
} }
f->fmt.pix.field = field; f->fmt.pix.field = field;
if (f->fmt.pix.width < 48)
f->fmt.pix.width = 48;
if (f->fmt.pix.height < 32) if (f->fmt.pix.height < 32)
f->fmt.pix.height = 32; f->fmt.pix.height = 32;
if (f->fmt.pix.width > maxw)
f->fmt.pix.width = maxw;
if (f->fmt.pix.height > maxh) if (f->fmt.pix.height > maxh)
f->fmt.pix.height = maxh; f->fmt.pix.height = maxh;
if (f->fmt.pix.width < 48)
f->fmt.pix.width = 48;
if (f->fmt.pix.width > maxw)
f->fmt.pix.width = maxw;
f->fmt.pix.width &= ~0x03;
f->fmt.pix.bytesperline = f->fmt.pix.bytesperline =
(f->fmt.pix.width * fmt->depth) >> 3; (f->fmt.pix.width * fmt->depth) >> 3;
f->fmt.pix.sizeimage = f->fmt.pix.sizeimage =
...@@ -1783,7 +1809,11 @@ static int video_do_ioctl(struct inode *inode, struct file *file, ...@@ -1783,7 +1809,11 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
return -EINVAL; return -EINVAL;
down(&dev->lock); down(&dev->lock);
dev->freq = f->frequency; dev->freq = f->frequency;
#ifdef V4L2_I2C_CLIENTS
cx8800_call_i2c_clients(dev,VIDIOC_S_FREQUENCY,f);
#else
cx8800_call_i2c_clients(dev,VIDIOCSFREQ,&dev->freq); cx8800_call_i2c_clients(dev,VIDIOCSFREQ,&dev->freq);
#endif
up(&dev->lock); up(&dev->lock);
return 0; return 0;
} }
...@@ -1885,7 +1915,6 @@ static int radio_do_ioctl(struct inode *inode, struct file *file, ...@@ -1885,7 +1915,6 @@ static int radio_do_ioctl(struct inode *inode, struct file *file,
case VIDIOC_G_TUNER: case VIDIOC_G_TUNER:
{ {
struct v4l2_tuner *t = arg; struct v4l2_tuner *t = arg;
struct video_tuner vt;
if (t->index > 0) if (t->index > 0)
return -EINVAL; return -EINVAL;
...@@ -1895,9 +1924,16 @@ static int radio_do_ioctl(struct inode *inode, struct file *file, ...@@ -1895,9 +1924,16 @@ static int radio_do_ioctl(struct inode *inode, struct file *file,
t->rangelow = (int)(65*16); t->rangelow = (int)(65*16);
t->rangehigh = (int)(108*16); t->rangehigh = (int)(108*16);
memset(&vt,0,sizeof(vt)); #ifdef V4L2_I2C_CLIENTS
cx8800_call_i2c_clients(dev,VIDIOCGTUNER,&vt); cx8800_call_i2c_clients(dev,VIDIOC_G_TUNER,t);
t->signal = vt.signal; #else
{
struct video_tuner vt;
memset(&vt,0,sizeof(vt));
cx8800_call_i2c_clients(dev,VIDIOCGTUNER,&vt);
t->signal = vt.signal;
}
#endif
return 0; return 0;
} }
case VIDIOC_ENUMINPUT: case VIDIOC_ENUMINPUT:
...@@ -2281,11 +2317,6 @@ static void cx8800_unregister_video(struct cx8800_dev *dev) ...@@ -2281,11 +2317,6 @@ static void cx8800_unregister_video(struct cx8800_dev *dev)
} }
} }
/* debug that damn oops ... */
static unsigned int oops = 0;
MODULE_PARM(oops,"i");
#define OOPS(msg) if (oops) printk("%s: %s\n",__FUNCTION__,msg);
static int __devinit cx8800_initdev(struct pci_dev *pci_dev, static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id) const struct pci_device_id *pci_id)
{ {
...@@ -2299,7 +2330,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ...@@ -2299,7 +2330,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
memset(dev,0,sizeof(*dev)); memset(dev,0,sizeof(*dev));
/* pci init */ /* pci init */
OOPS("pci init");
dev->pci = pci_dev; dev->pci = pci_dev;
if (pci_enable_device(pci_dev)) { if (pci_enable_device(pci_dev)) {
err = -EIO; err = -EIO;
...@@ -2308,7 +2338,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ...@@ -2308,7 +2338,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
sprintf(dev->name,"cx%x[%d]",pci_dev->device,cx8800_devcount); sprintf(dev->name,"cx%x[%d]",pci_dev->device,cx8800_devcount);
/* pci quirks */ /* pci quirks */
OOPS("pci quirks");
cx88_pci_quirks(dev->name, dev->pci, &latency); cx88_pci_quirks(dev->name, dev->pci, &latency);
if (UNSET != latency) { if (UNSET != latency) {
printk(KERN_INFO "%s: setting pci latency timer to %d\n", printk(KERN_INFO "%s: setting pci latency timer to %d\n",
...@@ -2317,7 +2346,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ...@@ -2317,7 +2346,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
} }
/* print pci info */ /* print pci info */
OOPS("pci info");
pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, " printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, "
...@@ -2333,14 +2361,15 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ...@@ -2333,14 +2361,15 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
} }
/* board config */ /* board config */
OOPS("board config");
dev->board = card[cx8800_devcount]; dev->board = card[cx8800_devcount];
for (i = 0; UNSET == dev->board && i < cx88_idcount; i++) for (i = 0; UNSET == dev->board && i < cx88_idcount; i++)
if (pci_dev->subsystem_vendor == cx88_subids[i].subvendor && if (pci_dev->subsystem_vendor == cx88_subids[i].subvendor &&
pci_dev->subsystem_device == cx88_subids[i].subdevice) pci_dev->subsystem_device == cx88_subids[i].subdevice)
dev->board = cx88_subids[i].card; dev->board = cx88_subids[i].card;
if (UNSET == dev->board) if (UNSET == dev->board) {
dev->board = CX88_BOARD_UNKNOWN; dev->board = CX88_BOARD_UNKNOWN;
cx88_card_list(dev);
}
printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
dev->name,pci_dev->subsystem_vendor, dev->name,pci_dev->subsystem_vendor,
pci_dev->subsystem_device,cx88_boards[dev->board].name, pci_dev->subsystem_device,cx88_boards[dev->board].name,
...@@ -2352,7 +2381,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ...@@ -2352,7 +2381,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
dev->tuner_type = cx88_boards[dev->board].tuner_type; dev->tuner_type = cx88_boards[dev->board].tuner_type;
/* get mmio */ /* get mmio */
OOPS("get mmio");
if (!request_mem_region(pci_resource_start(pci_dev,0), if (!request_mem_region(pci_resource_start(pci_dev,0),
pci_resource_len(pci_dev,0), pci_resource_len(pci_dev,0),
dev->name)) { dev->name)) {
...@@ -2366,7 +2394,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ...@@ -2366,7 +2394,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
dev->bmmio = (u8*)dev->lmmio; dev->bmmio = (u8*)dev->lmmio;
/* initialize driver struct */ /* initialize driver struct */
OOPS("init structs");
init_MUTEX(&dev->lock); init_MUTEX(&dev->lock);
dev->slock = SPIN_LOCK_UNLOCKED; dev->slock = SPIN_LOCK_UNLOCKED;
dev->tvnorm = tvnorms; dev->tvnorm = tvnorms;
...@@ -2390,11 +2417,9 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ...@@ -2390,11 +2417,9 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
MO_VID_DMACNTRL,0x88,0x00); MO_VID_DMACNTRL,0x88,0x00);
/* initialize hardware */ /* initialize hardware */
OOPS("reset hardware");
cx8800_reset(dev); cx8800_reset(dev);
/* get irq */ /* get irq */
OOPS("install irq handler");
err = request_irq(pci_dev->irq, cx8800_irq, err = request_irq(pci_dev->irq, cx8800_irq,
SA_SHIRQ | SA_INTERRUPT, dev->name, dev); SA_SHIRQ | SA_INTERRUPT, dev->name, dev);
if (err < 0) { if (err < 0) {
...@@ -2404,13 +2429,10 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ...@@ -2404,13 +2429,10 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
} }
/* register i2c bus + load i2c helpers */ /* register i2c bus + load i2c helpers */
OOPS("i2c setup");
cx8800_i2c_init(dev); cx8800_i2c_init(dev);
OOPS("card setup");
cx88_card_setup(dev); cx88_card_setup(dev);
/* load and configure helper modules */ /* load and configure helper modules */
OOPS("configure i2c clients");
if (TUNER_ABSENT != dev->tuner_type) if (TUNER_ABSENT != dev->tuner_type)
request_module("tuner"); request_module("tuner");
if (cx88_boards[dev->board].needs_tda9887) if (cx88_boards[dev->board].needs_tda9887)
...@@ -2419,7 +2441,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ...@@ -2419,7 +2441,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
cx8800_call_i2c_clients(dev,TUNER_SET_TYPE,&dev->tuner_type); cx8800_call_i2c_clients(dev,TUNER_SET_TYPE,&dev->tuner_type);
/* register v4l devices */ /* register v4l devices */
OOPS("register video");
dev->video_dev = vdev_init(dev,&cx8800_video_template,"video"); dev->video_dev = vdev_init(dev,&cx8800_video_template,"video");
err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
video_nr[cx8800_devcount]); video_nr[cx8800_devcount]);
...@@ -2431,7 +2452,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ...@@ -2431,7 +2452,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
printk(KERN_INFO "%s: registered device video%d [v4l2]\n", printk(KERN_INFO "%s: registered device video%d [v4l2]\n",
dev->name,dev->video_dev->minor & 0x1f); dev->name,dev->video_dev->minor & 0x1f);
OOPS("register vbi");
dev->vbi_dev = vdev_init(dev,&cx8800_vbi_template,"vbi"); dev->vbi_dev = vdev_init(dev,&cx8800_vbi_template,"vbi");
err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI, err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
vbi_nr[cx8800_devcount]); vbi_nr[cx8800_devcount]);
...@@ -2444,7 +2464,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ...@@ -2444,7 +2464,6 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
dev->name,dev->vbi_dev->minor & 0x1f); dev->name,dev->vbi_dev->minor & 0x1f);
if (dev->has_radio) { if (dev->has_radio) {
OOPS("register radio");
dev->radio_dev = vdev_init(dev,&cx8800_radio_template,"radio"); dev->radio_dev = vdev_init(dev,&cx8800_radio_template,"radio");
err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO, err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
radio_nr[cx8800_devcount]); radio_nr[cx8800_devcount]);
...@@ -2458,32 +2477,31 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, ...@@ -2458,32 +2477,31 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
} }
/* everything worked */ /* everything worked */
OOPS("finalize");
list_add_tail(&dev->devlist,&cx8800_devlist); list_add_tail(&dev->devlist,&cx8800_devlist);
pci_set_drvdata(pci_dev,dev); pci_set_drvdata(pci_dev,dev);
cx8800_devcount++; cx8800_devcount++;
/* initial device configuration */ /* initial device configuration */
OOPS("init device");
down(&dev->lock); down(&dev->lock);
init_controls(dev); init_controls(dev);
set_tvnorm(dev,tvnorms); set_tvnorm(dev,tvnorms);
video_mux(dev,0); video_mux(dev,0);
up(&dev->lock); up(&dev->lock);
/* start tvaudio thread */
init_completion(&dev->texit);
dev->tpid = kernel_thread(cx88_audio_thread, dev, 0);
return 0; return 0;
fail3: fail3:
OOPS("fail3");
cx8800_unregister_video(dev); cx8800_unregister_video(dev);
if (0 == dev->i2c_rc) if (0 == dev->i2c_rc)
i2c_bit_del_bus(&dev->i2c_adap); i2c_bit_del_bus(&dev->i2c_adap);
free_irq(pci_dev->irq, dev); free_irq(pci_dev->irq, dev);
fail2: fail2:
OOPS("fail2");
release_mem_region(pci_resource_start(pci_dev,0), release_mem_region(pci_resource_start(pci_dev,0),
pci_resource_len(pci_dev,0)); pci_resource_len(pci_dev,0));
fail1: fail1:
OOPS("fail1");
kfree(dev); kfree(dev);
return err; return err;
} }
...@@ -2492,6 +2510,11 @@ static void __devexit cx8800_finidev(struct pci_dev *pci_dev) ...@@ -2492,6 +2510,11 @@ static void __devexit cx8800_finidev(struct pci_dev *pci_dev)
{ {
struct cx8800_dev *dev = pci_get_drvdata(pci_dev); struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
/* stop thread */
dev->shutdown = 1;
if (dev->tpid >= 0)
wait_for_completion(&dev->texit);
cx8800_shutdown(dev); cx8800_shutdown(dev);
pci_disable_device(pci_dev); pci_disable_device(pci_dev);
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
#include "cx88-reg.h" #include "cx88-reg.h"
#include <linux/version.h> #include <linux/version.h>
#define CX88_VERSION_CODE KERNEL_VERSION(0,0,3) #define CX88_VERSION_CODE KERNEL_VERSION(0,0,4)
#ifndef TRUE #ifndef TRUE
# define TRUE (1==1) # define TRUE (1==1)
...@@ -114,17 +114,20 @@ extern struct sram_channel cx88_sram_channels[]; ...@@ -114,17 +114,20 @@ extern struct sram_channel cx88_sram_channels[];
/* card configuration */ /* card configuration */
#define CX88_BOARD_NOAUTO UNSET #define CX88_BOARD_NOAUTO UNSET
#define CX88_BOARD_UNKNOWN 0 #define CX88_BOARD_UNKNOWN 0
#define CX88_BOARD_HAUPPAUGE 1 #define CX88_BOARD_HAUPPAUGE 1
#define CX88_BOARD_GDI 2 #define CX88_BOARD_GDI 2
#define CX88_BOARD_PIXELVIEW 3 #define CX88_BOARD_PIXELVIEW 3
#define CX88_BOARD_ATI_WONDER_PRO 4 #define CX88_BOARD_ATI_WONDER_PRO 4
#define CX88_BOARD_WINFAST2000XP 5 #define CX88_BOARD_WINFAST2000XP 5
#define CX88_BOARD_AVERTV_303 6 #define CX88_BOARD_AVERTV_303 6
#define CX88_BOARD_MSI_TVANYWHERE 7 #define CX88_BOARD_MSI_TVANYWHERE_MASTER 7
#define CX88_BOARD_WINFAST_DV2000 8 #define CX88_BOARD_WINFAST_DV2000 8
#define CX88_BOARD_LEADTEK_PVR2000 9 #define CX88_BOARD_LEADTEK_PVR2000 9
#define CX88_BOARD_IODATA_GVVCP3PCI 10
#define CX88_BOARD_PROLINK_PLAYTVPVR 11
#define CX88_BOARD_ASUS_PVR_416 12
#define CX88_BOARD_MSI_TVANYWHERE 13
enum cx88_itype { enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1, CX88_VMUX_COMPOSITE1 = 1,
...@@ -263,6 +266,9 @@ struct cx8800_dev { ...@@ -263,6 +266,9 @@ struct cx8800_dev {
/* other global state info */ /* other global state info */
u32 shadow[SHADOW_MAX]; u32 shadow[SHADOW_MAX];
int shutdown;
pid_t tpid;
struct completion texit;
struct cx8800_suspend_state state; struct cx8800_suspend_state state;
}; };
...@@ -351,7 +357,8 @@ extern const unsigned int cx88_bcount; ...@@ -351,7 +357,8 @@ extern const unsigned int cx88_bcount;
extern struct cx88_subid cx88_subids[]; extern struct cx88_subid cx88_subids[];
extern const unsigned int cx88_idcount; extern const unsigned int cx88_idcount;
extern void __devinit cx88_card_setup(struct cx8800_dev *dev); extern void cx88_card_list(struct cx8800_dev *dev);
extern void cx88_card_setup(struct cx8800_dev *dev);
/* ----------------------------------------------------------- */ /* ----------------------------------------------------------- */
/* cx88-tvaudio.c */ /* cx88-tvaudio.c */
...@@ -372,6 +379,7 @@ extern void __devinit cx88_card_setup(struct cx8800_dev *dev); ...@@ -372,6 +379,7 @@ extern void __devinit cx88_card_setup(struct cx8800_dev *dev);
void cx88_set_tvaudio(struct cx8800_dev *dev); void cx88_set_tvaudio(struct cx8800_dev *dev);
void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t); void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t);
void cx88_set_stereo(struct cx8800_dev *dev, u32 mode); void cx88_set_stereo(struct cx8800_dev *dev, u32 mode);
int cx88_audio_thread(void *data);
/* /*
* Local variables: * Local variables:
......
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