From: Gerd Knorr 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: Andrew Morton --- 25-akpm/drivers/media/video/cx88/cx88-cards.c | 176 +++++++++- 25-akpm/drivers/media/video/cx88/cx88-i2c.c | 2 25-akpm/drivers/media/video/cx88/cx88-reg.h | 10 25-akpm/drivers/media/video/cx88/cx88-tvaudio.c | 397 ++++++++++++++---------- 25-akpm/drivers/media/video/cx88/cx88-vbi.c | 13 25-akpm/drivers/media/video/cx88/cx88-video.c | 175 +++++----- 25-akpm/drivers/media/video/cx88/cx88.h | 34 +- 7 files changed, 537 insertions(+), 270 deletions(-) diff -puN drivers/media/video/cx88/cx88-cards.c~v4l-cx88-driver-update drivers/media/video/cx88/cx88-cards.c --- 25/drivers/media/video/cx88/cx88-cards.c~v4l-cx88-driver-update 2004-06-19 13:58:49.171342696 -0700 +++ 25-akpm/drivers/media/video/cx88/cx88-cards.c 2004-06-19 13:58:49.185340568 -0700 @@ -99,6 +99,10 @@ struct cx88_board cx88_boards[] = { .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, + .gpio0 = 0x000003ff, + .gpio1 = 0x000000ff, + .gpio2 = 0x000000ff, + .gpio3 = 0x00000000, },{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, @@ -111,6 +115,7 @@ struct cx88_board cx88_boards[] = { [CX88_BOARD_WINFAST2000XP] = { .name = "Leadtek Winfast 2000XP Expert", .tuner_type = 44, + .needs_tda9887 = 1, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -149,22 +154,33 @@ struct cx88_board cx88_boards[] = { .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", .tuner_type = 33, + .needs_tda9887 = 1, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, + .gpio0 = 0x000040bf, + .gpio1 = 0x000080c0, + .gpio2 = 0x0000ff40, + .gpio3 = 0x00000000, },{ .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - },{ - // temporarly for testing ... - .type = CX88_VMUX_COMPOSITE2, - .vmux = 2, + .gpio0 = 0x000040bf, + .gpio1 = 0x000080c0, + .gpio2 = 0x0000ff40, + .gpio3 = 0x00000000, },{ .type = CX88_VMUX_SVIDEO, .vmux = 2, + .gpio0 = 0x000040bf, + .gpio1 = 0x000080c0, + .gpio2 = 0x0000ff40, + .gpio3 = 0x00000000, }}, .radio = { .type = CX88_RADIO, @@ -199,8 +215,97 @@ struct cx88_board cx88_boards[] = { .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); @@ -242,6 +347,10 @@ struct cx88_subid cx88_subids[] = { .card = CX88_BOARD_WINFAST_DV2000, },{ .subvendor = 0x107d, + .subdevice = 0x663b, + .card = CX88_BOARD_LEADTEK_PVR2000, + },{ + .subvendor = 0x107d, .subdevice = 0x663C, .card = CX88_BOARD_LEADTEK_PVR2000, },{ @@ -251,12 +360,19 @@ struct cx88_subid cx88_subids[] = { },{ .subvendor = 0x1462, .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); - /* ----------------------------------------------------------------------- */ /* some leadtek specific stuff */ @@ -386,20 +502,22 @@ static struct { [ 0x02 ] = { .id = TUNER_ABSENT, .name = "PAL_B" }, [ 0x03 ] = { .id = TUNER_ABSENT, - .name = "BAL_I" }, + .name = "PAL_I" }, [ 0x04 ] = { .id = TUNER_ABSENT, .name = "PAL_D" }, [ 0x05 ] = { .id = TUNER_ABSENT, .name = "SECAM" }, - [ 0x10 ] = { .id = TUNER_ABSENT, .fm = 1, + [ 0x10 ] = { .id = TUNER_ABSENT, + .fm = 1, .name = "TEMIC_4049" }, [ 0x11 ] = { .id = TUNER_TEMIC_4136FY5, .name = "TEMIC_4136" }, [ 0x12 ] = { .id = TUNER_ABSENT, .name = "TEMIC_4146" }, - [ 0x20 ] = { .id = TUNER_PHILIPS_FQ1216ME, .fm = 1, + [ 0x20 ] = { .id = TUNER_PHILIPS_FQ1216ME, + .fm = 1, .name = "PHILIPS_FQ1216_MK3" }, [ 0x21 ] = { .id = TUNER_ABSENT, .fm = 1, .name = "PHILIPS_FQ1236_MK3" }, @@ -454,7 +572,33 @@ i2c_eeprom(struct i2c_client *c, unsigne 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= 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= 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= 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]; @@ -474,6 +618,9 @@ void __devinit cx88_card_setup(struct cx i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom)); leadtek_eeprom(dev,eeprom); break; + case CX88_BOARD_ASUS_PVR_416: + dev->has_radio = 1; + break; } } @@ -483,6 +630,7 @@ EXPORT_SYMBOL(cx88_boards); EXPORT_SYMBOL(cx88_bcount); EXPORT_SYMBOL(cx88_subids); EXPORT_SYMBOL(cx88_idcount); +EXPORT_SYMBOL(cx88_card_list); EXPORT_SYMBOL(cx88_card_setup); /* diff -puN drivers/media/video/cx88/cx88.h~v4l-cx88-driver-update drivers/media/video/cx88/cx88.h --- 25/drivers/media/video/cx88/cx88.h~v4l-cx88-driver-update 2004-06-19 13:58:49.173342392 -0700 +++ 25-akpm/drivers/media/video/cx88/cx88.h 2004-06-19 13:58:49.197338744 -0700 @@ -32,7 +32,7 @@ #include "cx88-reg.h" #include -#define CX88_VERSION_CODE KERNEL_VERSION(0,0,3) +#define CX88_VERSION_CODE KERNEL_VERSION(0,0,4) #ifndef TRUE # define TRUE (1==1) @@ -114,17 +114,20 @@ extern struct sram_channel cx88_sram_cha /* card configuration */ #define CX88_BOARD_NOAUTO UNSET -#define CX88_BOARD_UNKNOWN 0 -#define CX88_BOARD_HAUPPAUGE 1 -#define CX88_BOARD_GDI 2 -#define CX88_BOARD_PIXELVIEW 3 -#define CX88_BOARD_ATI_WONDER_PRO 4 -#define CX88_BOARD_WINFAST2000XP 5 -#define CX88_BOARD_AVERTV_303 6 -#define CX88_BOARD_MSI_TVANYWHERE 7 -#define CX88_BOARD_WINFAST_DV2000 8 -#define CX88_BOARD_LEADTEK_PVR2000 9 - +#define CX88_BOARD_UNKNOWN 0 +#define CX88_BOARD_HAUPPAUGE 1 +#define CX88_BOARD_GDI 2 +#define CX88_BOARD_PIXELVIEW 3 +#define CX88_BOARD_ATI_WONDER_PRO 4 +#define CX88_BOARD_WINFAST2000XP 5 +#define CX88_BOARD_AVERTV_303 6 +#define CX88_BOARD_MSI_TVANYWHERE_MASTER 7 +#define CX88_BOARD_WINFAST_DV2000 8 +#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 { CX88_VMUX_COMPOSITE1 = 1, @@ -263,6 +266,9 @@ struct cx8800_dev { /* other global state info */ u32 shadow[SHADOW_MAX]; + int shutdown; + pid_t tpid; + struct completion texit; struct cx8800_suspend_state state; }; @@ -351,7 +357,8 @@ extern const unsigned int cx88_bcount; extern struct cx88_subid cx88_subids[]; 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 */ @@ -372,6 +379,7 @@ extern void __devinit cx88_card_setup(st void cx88_set_tvaudio(struct cx8800_dev *dev); void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t); void cx88_set_stereo(struct cx8800_dev *dev, u32 mode); +int cx88_audio_thread(void *data); /* * Local variables: diff -puN drivers/media/video/cx88/cx88-i2c.c~v4l-cx88-driver-update drivers/media/video/cx88/cx88-i2c.c --- 25/drivers/media/video/cx88/cx88-i2c.c~v4l-cx88-driver-update 2004-06-19 13:58:49.174342240 -0700 +++ 25-akpm/drivers/media/video/cx88/cx88-i2c.c 2004-06-19 13:58:49.186340416 -0700 @@ -22,8 +22,6 @@ */ -#define __NO_VERSION__ 1 - #include #include diff -puN drivers/media/video/cx88/cx88-reg.h~v4l-cx88-driver-update drivers/media/video/cx88/cx88-reg.h --- 25/drivers/media/video/cx88/cx88-reg.h~v4l-cx88-driver-update 2004-06-19 13:58:49.176341936 -0700 +++ 25-akpm/drivers/media/video/cx88/cx88-reg.h 2004-06-19 13:58:49.187340264 -0700 @@ -599,10 +599,20 @@ #define EN_I2SIN_STR2DAC 0x00004000 #define EN_I2SIN_ENABLE 0x00008000 +#if 0 +/* old */ #define EN_DMTRX_SUMDIFF 0x00000800 #define EN_DMTRX_SUMR 0x00000880 #define EN_DMTRX_LR 0x00000900 #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 #define VID_CAPTURE_CONTROL 0x310180 diff -puN drivers/media/video/cx88/cx88-tvaudio.c~v4l-cx88-driver-update drivers/media/video/cx88/cx88-tvaudio.c --- 25/drivers/media/video/cx88/cx88-tvaudio.c~v4l-cx88-driver-update 2004-06-19 13:58:49.177341784 -0700 +++ 25-akpm/drivers/media/video/cx88/cx88-tvaudio.c 2004-06-19 13:58:49.192339504 -0700 @@ -48,6 +48,7 @@ #include #include #include +#include #include "cx88.h" @@ -60,6 +61,33 @@ MODULE_PARM_DESC(audio_debug,"enable deb /* ----------------------------------------------------------- */ +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 { u32 reg; u32 val; @@ -125,26 +153,116 @@ static void set_audio_finish(struct cx88 static void set_audio_standard_BTSC(struct cx8800_dev *dev, unsigned int sap) { static const struct rlist btsc[] = { - /* Magic stuff from leadtek driver + datasheet.*/ - { AUD_DBX_IN_GAIN, 0x4734 }, - { AUD_DBX_WBE_GAIN, 0x4640 }, - { AUD_DBX_SE_GAIN, 0x8D31 }, - { AUD_DEEMPH0_G0, 0x1604 }, - { AUD_PHASE_FIX_CTL, 0x0020 }, - + /* from dscaler */ + { AUD_OUT1_SEL, 0x00000013 }, + { AUD_OUT1_SHIFT, 0x00000000 }, + { AUD_POLY0_DDS_CONSTANT, 0x0012010c }, + { AUD_DMD_RA_DDS, 0x00c3e7aa }, + { 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 */ }, }; - dprintk("%s (status: unknown)\n",__FUNCTION__); - set_audio_start(dev, 0x0001, - EN_BTSC_AUTO_STEREO); - set_audio_registers(dev, btsc); + // dscaler: exactly taken from driver, + // dscaler: don't know why to set EN_FMRADIO_EN_RDS + if (sap) { + 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); } 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_ADJ2, 0x00000040 }, { AUD_RATE_ADJ3, 0x00000100 }, @@ -152,21 +270,64 @@ static void set_audio_standard_NICAM(str { AUD_RATE_ADJ5, 0x00001000 }, // { 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 { AUD_PDF_DDS_CNST_BYTE2, 0x06 }, { AUD_PDF_DDS_CNST_BYTE1, 0x82 }, { AUD_PDF_DDS_CNST_BYTE0, 0x16 }, { 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_8LSB, 0x4c }, { /* end of list */ }, - }; + }; - dprintk("%s (status: unknown)\n",__FUNCTION__); set_audio_start(dev, 0x0010, - EN_DMTRX_LR | EN_NICAM_FORCE_STEREO); - set_audio_registers(dev, nicam); + EN_DMTRX_LR | EN_DMTRX_BYPASS | EN_NICAM_AUTO_STEREO); + 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); } @@ -297,7 +458,7 @@ static void set_audio_standard_NICAM_L(s static void set_audio_standard_A2(struct cx8800_dev *dev) { /* from dscaler cvs */ - static const struct rlist a2[] = { + static const struct rlist a2_common[] = { { AUD_PDF_DDS_CNST_BYTE2, 0x06 }, { AUD_PDF_DDS_CNST_BYTE1, 0x82 }, { AUD_PDF_DDS_CNST_BYTE0, 0x12 }, @@ -347,16 +508,20 @@ static void set_audio_standard_A2(struct { AUD_RDSQ_SHIFT, 0x00000000 }, { AUD_POLYPH80SCALEFAC, 0x00000001 }, - // Table 1 + { /* end of list */ }, + }; + + static const struct rlist a2_table1[] = { + // PAL-BG { AUD_DMD_RA_DDS, 0x002a73bd }, { AUD_C1_UP_THR, 0x00007000 }, { AUD_C1_LO_THR, 0x00005400 }, { AUD_C2_UP_THR, 0x00005400 }, { AUD_C2_LO_THR, 0x00003000 }, - -#if 0 - // found this in WDM-driver for A2, must country spec. - // Table 2 + { /* end of list */ }, + }; + static const struct rlist a2_table2[] = { + // PAL-DK { AUD_DMD_RA_DDS, 0x002a73bd }, { AUD_C1_UP_THR, 0x00007000 }, { AUD_C1_LO_THR, 0x00005400 }, @@ -364,8 +529,10 @@ static void set_audio_standard_A2(struct { AUD_C2_LO_THR, 0x00003000 }, { AUD_DN0_FREQ, 0x00003a1c }, { AUD_DN2_FREQ, 0x0000d2e0 }, - - // Table 3 + { /* end of list */ }, + }; + static const struct rlist a2_table3[] = { + // unknown, probably NTSC-M { AUD_DMD_RA_DDS, 0x002a2873 }, { AUD_C1_UP_THR, 0x00003c00 }, { AUD_C1_LO_THR, 0x00003000 }, @@ -375,140 +542,26 @@ static void set_audio_standard_A2(struct { AUD_DN1_FREQ, 0x00003418 }, { AUD_DN2_FREQ, 0x000029c7 }, { AUD_POLY0_DDS_CONSTANT, 0x000a7540 }, -#endif - { /* end of list */ }, }; - static const struct rlist a2_old[] = { - { AUD_DN0_FREQ, 0x0000312b }, - { AUD_POLY0_DDS_CONSTANT, 0x000a62b4 }, - { AUD_IIR1_0_SEL, 0x00000000 }, - { AUD_IIR1_1_SEL, 0x00000001 }, - { AUD_IIR1_2_SEL, 0x0000001f }, - { AUD_IIR1_3_SEL, 0x00000020 }, - { AUD_IIR1_4_SEL, 0x00000023 }, - { AUD_IIR1_5_SEL, 0x00000007 }, - { AUD_IIR1_0_SHIFT, 0x00000000 }, - { AUD_IIR1_1_SHIFT, 0x00000000 }, - { AUD_IIR1_2_SHIFT, 0x00000007 }, - { AUD_IIR1_3_SHIFT, 0x00000007 }, - { AUD_IIR1_4_SHIFT, 0x00000007 }, - { AUD_IIR1_5_SHIFT, 0x00000000 }, - { 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_start(dev, 0x0004, EN_DMTRX_SUMDIFF | EN_A2_AUTO_STEREO); + set_audio_registers(dev, a2_common); + switch (dev->tvaudio) { + case WW_A2_BG: + dprintk("%s PAL-BG A2 (status: known-good)\n",__FUNCTION__); + set_audio_registers(dev, a2_table1); + break; + case WW_A2_DK: + dprintk("%s PAL-DK A2 (status: known-good)\n",__FUNCTION__); + set_audio_registers(dev, a2_table2); + break; + case WW_A2_M: + dprintk("%s NTSC-M A2 (status: unknown)\n",__FUNCTION__); + set_audio_registers(dev, a2_table3); + break; }; - - - 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); - } + set_audio_finish(dev); } static void set_audio_standard_EIAJ(struct cx8800_dev *dev) @@ -617,9 +670,9 @@ void cx88_get_stereo(struct cx8800_dev * reg = cx_read(AUD_STATUS); mode = reg & 0x03; pilot = (reg >> 2) & 0x03; - dprintk("AUD_STATUS: %s / %s [status=0x%x,ctl=0x%x,vol=0x%x]\n", - m[mode], p[pilot], reg, - cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL)); + dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n", + reg, m[mode], p[pilot], + aud_ctl_names[cx_read(AUD_CTL) & 63]); t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; @@ -628,6 +681,8 @@ void cx88_get_stereo(struct cx8800_dev * switch (dev->tvaudio) { case WW_A2_BG: + case WW_A2_DK: + case WW_A2_M: if (1 == pilot) { /* stereo */ t->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; @@ -659,6 +714,8 @@ void cx88_set_stereo(struct cx8800_dev * switch (dev->tvaudio) { case WW_A2_BG: + case WW_A2_DK: + case WW_A2_M: switch (mode) { case V4L2_TUNER_MODE_MONO: case V4L2_TUNER_MODE_LANG1: @@ -717,6 +774,32 @@ void cx88_set_stereo(struct cx8800_dev * 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: * c-basic-offset: 8 diff -puN drivers/media/video/cx88/cx88-vbi.c~v4l-cx88-driver-update drivers/media/video/cx88/cx88-vbi.c --- 25/drivers/media/video/cx88/cx88-vbi.c~v4l-cx88-driver-update 2004-06-19 13:58:49.179341480 -0700 +++ 25-akpm/drivers/media/video/cx88/cx88-vbi.c 2004-06-19 13:58:49.192339504 -0700 @@ -28,17 +28,14 @@ void cx8800_vbi_fmt(struct cx8800_dev *d f->fmt.vbi.count[0] = VBI_LINE_COUNT; f->fmt.vbi.count[1] = VBI_LINE_COUNT; - switch (dev->tvnorm->id) { - case V4L2_STD_NTSC_M: - case V4L2_STD_NTSC_M_JP: + if (dev->tvnorm->id & V4L2_STD_525_60) { + /* ntsc */ f->fmt.vbi.sampling_rate = 28636363; f->fmt.vbi.start[0] = 10 -1; f->fmt.vbi.start[1] = 273 -1; - break; - case V4L2_STD_PAL_BG: - case V4L2_STD_PAL_DK: - case V4L2_STD_PAL_I: - case V4L2_STD_SECAM: + + } else if (V4L2_STD_625_50) { + /* pal */ f->fmt.vbi.sampling_rate = 35468950; f->fmt.vbi.start[0] = 7 -1; f->fmt.vbi.start[1] = 319 -1; diff -puN drivers/media/video/cx88/cx88-video.c~v4l-cx88-driver-update drivers/media/video/cx88/cx88-video.c --- 25/drivers/media/video/cx88/cx88-video.c~v4l-cx88-driver-update 2004-06-19 13:58:49.180341328 -0700 +++ 25-akpm/drivers/media/video/cx88/cx88-video.c 2004-06-19 13:58:49.197338744 -0700 @@ -2,7 +2,7 @@ * device driver for Conexant 2388x based TV cards * video4linux video interface * - * (c) 2003 Gerd Knorr [SuSE Labs] + * (c) 2003-04 Gerd Knorr [SuSE Labs] * * 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 @@ -19,8 +19,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define __NO_VERSION__ 1 - #include #include #include @@ -32,6 +30,8 @@ #include "cx88.h" +#define V4L2_I2C_CLIENTS 1 + MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards"); MODULE_AUTHOR("Gerd Knorr [SuSE Labs]"); MODULE_LICENSE("GPL"); @@ -194,8 +194,13 @@ static struct cx8800_tvnorm tvnorms[] = .cxiformat = VideoFormatPAL60, .cxoformat = 0x181f0008, },{ - .name = "SECAM", - .id = V4L2_STD_SECAM, + .name = "SECAM-L", + .id = V4L2_STD_SECAM_L, + .cxiformat = VideoFormatSECAM, + .cxoformat = 0x181f0008, + },{ + .name = "SECAM-DK", + .id = V4L2_STD_SECAM_DK, .cxiformat = VideoFormatSECAM, .cxoformat = 0x181f0008, } @@ -483,35 +488,38 @@ static int set_tvaudio(struct cx8800_dev if (CX88_VMUX_TELEVISION != INPUT(dev->input)->type) return 0; - switch (dev->tvnorm->id) { - case V4L2_STD_PAL_BG: + if (V4L2_STD_PAL_BG & dev->tvnorm->id) { 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; - break; - case V4L2_STD_PAL_I: + + } else if (V4L2_STD_PAL_I & dev->tvnorm->id) { dev->tvaudio = WW_NICAM_I; - break; - case V4L2_STD_SECAM: - dev->tvaudio = WW_SYSTEM_L_AM; /* FIXME: fr != ru */ - break; - case V4L2_STD_NTSC_M: + + } else if (V4L2_STD_SECAM_L & dev->tvnorm->id) { + dev->tvaudio = WW_SYSTEM_L_AM; + + } 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; - break; - case V4L2_STD_NTSC_M_JP: + + } else if (V4L2_STD_NTSC_M_JP & dev->tvnorm->id) { dev->tvaudio = WW_EIAJ; - break; - default: - dprintk(1,"tvaudio support needs work for this tv norm [%s], sorry\n", - dev->tvnorm->name); + + } else { + printk("%s: tvaudio support needs work for this tv norm [%s], sorry\n", + dev->name, dev->tvnorm->name); dev->tvaudio = 0; return 0; } cx_andor(MO_AFECFG_IO, 0x1f, 0x0); 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_AUDR_LNGTH, 128/8); /* fifo size */ @@ -526,7 +534,6 @@ static int set_tvnorm(struct cx8800_dev u32 vdec_clock; u64 tmp64; u32 bdelay,agcdelay,htotal; - struct video_channel c; dev->tvnorm = norm; fsc8 = norm_fsc8(norm); @@ -592,30 +599,43 @@ static int set_tvnorm(struct cx8800_dev set_tvaudio(dev); // tell i2c chips - memset(&c,0,sizeof(c)); - c.channel = dev->input; - 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); +#ifdef V4L2_I2C_CLIENTS + cx8800_call_i2c_clients(dev,VIDIOC_S_STD,&norm->id); +#else + { + struct video_channel c; + memset(&c,0,sizeof(c)); + c.channel = dev->input; + 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 return 0; } 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 sheight = norm_maxh(dev->tvnorm); 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 value = (width * norm_hdelay(dev->tvnorm)) / swidth; + value &= 0x3fe; cx_write(MO_HDELAY_EVEN, value); cx_write(MO_HDELAY_ODD, value); dprintk(1,"set_scale: hdelay 0x%04x\n", value); @@ -646,7 +666,7 @@ static int set_scale(struct cx8800_dev * // setup filters value = 0; value |= (1 << 19); // CFILT (default) - if (interlaced) + if (V4L2_FIELD_INTERLACED == field) value |= (1 << 3); // VINT (interlaced vertical scaling) if (width < 385) value |= (1 << 0); // 3-tap interpolation @@ -675,10 +695,12 @@ static int video_mux(struct cx8800_dev * switch (INPUT(input)->type) { case CX88_VMUX_SVIDEO: - cx_andor(MO_AFECFG_IO, 0x01, 0x01); + cx_set(MO_AFECFG_IO, 0x00000001); + cx_set(MO_INPUT_FORMAT, 0x00010010); break; default: - cx_andor(MO_AFECFG_IO, 0x01, 0x00); + cx_clear(MO_AFECFG_IO, 0x00000001); + cx_clear(MO_INPUT_FORMAT, 0x00010010); break; } return 0; @@ -693,7 +715,7 @@ static int start_video_dma(struct cx8800 /* setup fifo + format */ cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH21], 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); /* reset counter */ @@ -1350,6 +1372,9 @@ static int get_control(struct cx8800_dev case V4L2_CID_AUDIO_BALANCE: ctl->value = (value & 0x40) ? (value & 0x3f) : (0x40 - (value & 0x3f)); break; + case V4L2_CID_AUDIO_VOLUME: + ctl->value = 0x3f - (value & 0x3f); + break; default: ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift; break; @@ -1378,6 +1403,9 @@ static int set_control(struct cx8800_dev case V4L2_CID_AUDIO_BALANCE: value = (ctl->value < 0x40) ? (0x40 - ctl->value) : ctl->value; break; + case V4L2_CID_AUDIO_VOLUME: + value = 0x3f - (ctl->value & 0x3f); + break; case V4L2_CID_SATURATION: /* special v_sat handling */ v_sat_value = ctl->value - (0x7f - 0x5a); @@ -1409,7 +1437,7 @@ static void init_controls(struct cx8800_ }; static struct v4l2_control volume = { .id = V4L2_CID_AUDIO_VOLUME, - .value = 0, + .value = 0x3f, }; set_control(dev,&mute); @@ -1459,15 +1487,12 @@ static int cx8800_try_fmt(struct cx8800_ maxw = norm_maxw(dev->tvnorm); maxh = norm_maxh(dev->tvnorm); -#if 0 if (V4L2_FIELD_ANY == field) { field = (f->fmt.pix.height > maxh/2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_BOTTOM; } -#else - field = V4L2_FIELD_INTERLACED; -#endif + switch (field) { case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: @@ -1480,14 +1505,15 @@ static int cx8800_try_fmt(struct cx8800_ } f->fmt.pix.field = field; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; if (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) 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.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = @@ -1783,7 +1809,11 @@ static int video_do_ioctl(struct inode * return -EINVAL; down(&dev->lock); 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); +#endif up(&dev->lock); return 0; } @@ -1885,7 +1915,6 @@ static int radio_do_ioctl(struct inode * case VIDIOC_G_TUNER: { struct v4l2_tuner *t = arg; - struct video_tuner vt; if (t->index > 0) return -EINVAL; @@ -1895,9 +1924,16 @@ static int radio_do_ioctl(struct inode * t->rangelow = (int)(65*16); t->rangehigh = (int)(108*16); - memset(&vt,0,sizeof(vt)); - cx8800_call_i2c_clients(dev,VIDIOCGTUNER,&vt); - t->signal = vt.signal; +#ifdef V4L2_I2C_CLIENTS + cx8800_call_i2c_clients(dev,VIDIOC_G_TUNER,t); +#else + { + struct video_tuner vt; + memset(&vt,0,sizeof(vt)); + cx8800_call_i2c_clients(dev,VIDIOCGTUNER,&vt); + t->signal = vt.signal; + } +#endif return 0; } case VIDIOC_ENUMINPUT: @@ -2281,11 +2317,6 @@ static void cx8800_unregister_video(stru } } -/* 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, const struct pci_device_id *pci_id) { @@ -2299,7 +2330,6 @@ static int __devinit cx8800_initdev(stru memset(dev,0,sizeof(*dev)); /* pci init */ - OOPS("pci init"); dev->pci = pci_dev; if (pci_enable_device(pci_dev)) { err = -EIO; @@ -2308,7 +2338,6 @@ static int __devinit cx8800_initdev(stru sprintf(dev->name,"cx%x[%d]",pci_dev->device,cx8800_devcount); /* pci quirks */ - OOPS("pci quirks"); cx88_pci_quirks(dev->name, dev->pci, &latency); if (UNSET != latency) { printk(KERN_INFO "%s: setting pci latency timer to %d\n", @@ -2317,7 +2346,6 @@ static int __devinit cx8800_initdev(stru } /* 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_LATENCY_TIMER, &dev->pci_lat); printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, " @@ -2333,14 +2361,15 @@ static int __devinit cx8800_initdev(stru } /* board config */ - OOPS("board config"); dev->board = card[cx8800_devcount]; for (i = 0; UNSET == dev->board && i < cx88_idcount; i++) if (pci_dev->subsystem_vendor == cx88_subids[i].subvendor && pci_dev->subsystem_device == cx88_subids[i].subdevice) dev->board = cx88_subids[i].card; - if (UNSET == dev->board) + if (UNSET == dev->board) { dev->board = CX88_BOARD_UNKNOWN; + cx88_card_list(dev); + } printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", dev->name,pci_dev->subsystem_vendor, pci_dev->subsystem_device,cx88_boards[dev->board].name, @@ -2352,7 +2381,6 @@ static int __devinit cx8800_initdev(stru dev->tuner_type = cx88_boards[dev->board].tuner_type; /* get mmio */ - OOPS("get mmio"); if (!request_mem_region(pci_resource_start(pci_dev,0), pci_resource_len(pci_dev,0), dev->name)) { @@ -2366,7 +2394,6 @@ static int __devinit cx8800_initdev(stru dev->bmmio = (u8*)dev->lmmio; /* initialize driver struct */ - OOPS("init structs"); init_MUTEX(&dev->lock); dev->slock = SPIN_LOCK_UNLOCKED; dev->tvnorm = tvnorms; @@ -2390,11 +2417,9 @@ static int __devinit cx8800_initdev(stru MO_VID_DMACNTRL,0x88,0x00); /* initialize hardware */ - OOPS("reset hardware"); cx8800_reset(dev); /* get irq */ - OOPS("install irq handler"); err = request_irq(pci_dev->irq, cx8800_irq, SA_SHIRQ | SA_INTERRUPT, dev->name, dev); if (err < 0) { @@ -2404,13 +2429,10 @@ static int __devinit cx8800_initdev(stru } /* register i2c bus + load i2c helpers */ - OOPS("i2c setup"); cx8800_i2c_init(dev); - OOPS("card setup"); cx88_card_setup(dev); /* load and configure helper modules */ - OOPS("configure i2c clients"); if (TUNER_ABSENT != dev->tuner_type) request_module("tuner"); if (cx88_boards[dev->board].needs_tda9887) @@ -2419,7 +2441,6 @@ static int __devinit cx8800_initdev(stru cx8800_call_i2c_clients(dev,TUNER_SET_TYPE,&dev->tuner_type); /* register v4l devices */ - OOPS("register video"); dev->video_dev = vdev_init(dev,&cx8800_video_template,"video"); err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, video_nr[cx8800_devcount]); @@ -2431,7 +2452,6 @@ static int __devinit cx8800_initdev(stru printk(KERN_INFO "%s: registered device video%d [v4l2]\n", dev->name,dev->video_dev->minor & 0x1f); - OOPS("register vbi"); dev->vbi_dev = vdev_init(dev,&cx8800_vbi_template,"vbi"); err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI, vbi_nr[cx8800_devcount]); @@ -2444,7 +2464,6 @@ static int __devinit cx8800_initdev(stru dev->name,dev->vbi_dev->minor & 0x1f); if (dev->has_radio) { - OOPS("register radio"); dev->radio_dev = vdev_init(dev,&cx8800_radio_template,"radio"); err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO, radio_nr[cx8800_devcount]); @@ -2458,32 +2477,31 @@ static int __devinit cx8800_initdev(stru } /* everything worked */ - OOPS("finalize"); list_add_tail(&dev->devlist,&cx8800_devlist); pci_set_drvdata(pci_dev,dev); cx8800_devcount++; /* initial device configuration */ - OOPS("init device"); down(&dev->lock); init_controls(dev); set_tvnorm(dev,tvnorms); video_mux(dev,0); up(&dev->lock); + + /* start tvaudio thread */ + init_completion(&dev->texit); + dev->tpid = kernel_thread(cx88_audio_thread, dev, 0); return 0; fail3: - OOPS("fail3"); cx8800_unregister_video(dev); if (0 == dev->i2c_rc) i2c_bit_del_bus(&dev->i2c_adap); free_irq(pci_dev->irq, dev); fail2: - OOPS("fail2"); release_mem_region(pci_resource_start(pci_dev,0), pci_resource_len(pci_dev,0)); fail1: - OOPS("fail1"); kfree(dev); return err; } @@ -2492,6 +2510,11 @@ static void __devexit cx8800_finidev(str { 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); pci_disable_device(pci_dev); _