diff options
Diffstat (limited to 'drivers/media/pci/saa7134')
-rw-r--r-- | drivers/media/pci/saa7134/Kconfig | 64 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/Makefile | 16 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa6752hs.c | 1012 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134-alsa.c | 1209 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134-cards.c | 8026 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134-core.c | 1368 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134-dvb.c | 1936 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134-empress.c | 590 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134-i2c.c | 435 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134-input.c | 1041 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134-reg.h | 378 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134-ts.c | 327 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134-tvaudio.c | 1087 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134-vbi.c | 255 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134-video.c | 2661 | ||||
-rw-r--r-- | drivers/media/pci/saa7134/saa7134.h | 855 |
16 files changed, 21260 insertions, 0 deletions
diff --git a/drivers/media/pci/saa7134/Kconfig b/drivers/media/pci/saa7134/Kconfig new file mode 100644 index 000000000000..15b90d6e9130 --- /dev/null +++ b/drivers/media/pci/saa7134/Kconfig @@ -0,0 +1,64 @@ +config VIDEO_SAA7134 + tristate "Philips SAA7134 support" + depends on VIDEO_DEV && PCI && I2C + select VIDEOBUF_DMA_SG + select VIDEO_TUNER + select VIDEO_TVEEPROM + select CRC32 + select VIDEO_SAA6588 if MEDIA_SUBDRV_AUTOSELECT + ---help--- + This is a video4linux driver for Philips SAA713x based + TV cards. + + To compile this driver as a module, choose M here: the + module will be called saa7134. + +config VIDEO_SAA7134_ALSA + tristate "Philips SAA7134 DMA audio support" + depends on VIDEO_SAA7134 && SND + select SND_PCM + ---help--- + This is a video4linux driver for direct (DMA) audio in + Philips SAA713x based TV cards using ALSA + + To compile this driver as a module, choose M here: the + module will be called saa7134-alsa. + +config VIDEO_SAA7134_RC + bool "Philips SAA7134 Remote Controller support" + depends on RC_CORE + depends on VIDEO_SAA7134 + depends on !(RC_CORE=m && VIDEO_SAA7134=y) + default y + ---help--- + Enables Remote Controller support on saa7134 driver. + +config VIDEO_SAA7134_DVB + tristate "DVB/ATSC Support for saa7134 based TV cards" + depends on VIDEO_SAA7134 && DVB_CORE + select VIDEOBUF_DVB + select DVB_PLL if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT352 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA1004X if MEDIA_SUBDRV_AUTOSELECT + select DVB_NXT200X if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10086 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA826X if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6421 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ISL6405 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA827X if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_SIMPLE if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10036 if MEDIA_SUBDRV_AUTOSELECT + select DVB_MT312 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LNBP21 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10353 if MEDIA_SUBDRV_AUTOSELECT + select DVB_LGDT3305 if MEDIA_SUBDRV_AUTOSELECT + select DVB_TDA10048 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA18271 if MEDIA_SUBDRV_AUTOSELECT + select MEDIA_TUNER_TDA8290 if MEDIA_SUBDRV_AUTOSELECT + select DVB_ZL10039 if MEDIA_SUBDRV_AUTOSELECT + ---help--- + This adds support for DVB cards based on the + Philips saa7134 chip. + + To compile this driver as a module, choose M here: the + module will be called saa7134-dvb. diff --git a/drivers/media/pci/saa7134/Makefile b/drivers/media/pci/saa7134/Makefile new file mode 100644 index 000000000000..35375480ed4d --- /dev/null +++ b/drivers/media/pci/saa7134/Makefile @@ -0,0 +1,16 @@ + +saa7134-y += saa7134-cards.o saa7134-core.o saa7134-i2c.o +saa7134-y += saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o +saa7134-y += saa7134-video.o +saa7134-$(CONFIG_VIDEO_SAA7134_RC) += saa7134-input.o + +obj-$(CONFIG_VIDEO_SAA7134) += saa6752hs.o saa7134.o saa7134-empress.o + +obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o + +obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o + +ccflags-y += -I$(srctree)/drivers/media/i2c +ccflags-y += -I$(srctree)/drivers/media/tuners +ccflags-y += -I$(srctree)/drivers/media/dvb-core +ccflags-y += -I$(srctree)/drivers/media/dvb-frontends diff --git a/drivers/media/pci/saa7134/saa6752hs.c b/drivers/media/pci/saa7134/saa6752hs.c new file mode 100644 index 000000000000..f147b05bd860 --- /dev/null +++ b/drivers/media/pci/saa7134/saa6752hs.c @@ -0,0 +1,1012 @@ + /* + saa6752hs - i2c-driver for the saa6752hs by Philips + + Copyright (C) 2004 Andrew de Quincey + + AC-3 support: + + Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License vs published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mvss Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/i2c.h> +#include <linux/types.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> +#include <linux/init.h> +#include <linux/crc32.h> + +#define MPEG_VIDEO_TARGET_BITRATE_MAX 27000 +#define MPEG_VIDEO_MAX_BITRATE_MAX 27000 +#define MPEG_TOTAL_TARGET_BITRATE_MAX 27000 +#define MPEG_PID_MAX ((1 << 14) - 1) + + +MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder"); +MODULE_AUTHOR("Andrew de Quincey"); +MODULE_LICENSE("GPL"); + +enum saa6752hs_videoformat { + SAA6752HS_VF_D1 = 0, /* standard D1 video format: 720x576 */ + SAA6752HS_VF_2_3_D1 = 1,/* 2/3D1 video format: 480x576 */ + SAA6752HS_VF_1_2_D1 = 2,/* 1/2D1 video format: 352x576 */ + SAA6752HS_VF_SIF = 3, /* SIF video format: 352x288 */ + SAA6752HS_VF_UNKNOWN, +}; + +struct saa6752hs_mpeg_params { + /* transport streams */ + __u16 ts_pid_pmt; + __u16 ts_pid_audio; + __u16 ts_pid_video; + __u16 ts_pid_pcr; + + /* audio */ + enum v4l2_mpeg_audio_encoding au_encoding; + enum v4l2_mpeg_audio_l2_bitrate au_l2_bitrate; + enum v4l2_mpeg_audio_ac3_bitrate au_ac3_bitrate; + + /* video */ + enum v4l2_mpeg_video_aspect vi_aspect; + enum v4l2_mpeg_video_bitrate_mode vi_bitrate_mode; + __u32 vi_bitrate; + __u32 vi_bitrate_peak; +}; + +static const struct v4l2_format v4l2_format_table[] = +{ + [SAA6752HS_VF_D1] = + { .fmt = { .pix = { .width = 720, .height = 576 }}}, + [SAA6752HS_VF_2_3_D1] = + { .fmt = { .pix = { .width = 480, .height = 576 }}}, + [SAA6752HS_VF_1_2_D1] = + { .fmt = { .pix = { .width = 352, .height = 576 }}}, + [SAA6752HS_VF_SIF] = + { .fmt = { .pix = { .width = 352, .height = 288 }}}, + [SAA6752HS_VF_UNKNOWN] = + { .fmt = { .pix = { .width = 0, .height = 0}}}, +}; + +struct saa6752hs_state { + struct v4l2_subdev sd; + int chip; + u32 revision; + int has_ac3; + struct saa6752hs_mpeg_params params; + enum saa6752hs_videoformat video_format; + v4l2_std_id standard; +}; + +enum saa6752hs_command { + SAA6752HS_COMMAND_RESET = 0, + SAA6752HS_COMMAND_STOP = 1, + SAA6752HS_COMMAND_START = 2, + SAA6752HS_COMMAND_PAUSE = 3, + SAA6752HS_COMMAND_RECONFIGURE = 4, + SAA6752HS_COMMAND_SLEEP = 5, + SAA6752HS_COMMAND_RECONFIGURE_FORCE = 6, + + SAA6752HS_COMMAND_MAX +}; + +static inline struct saa6752hs_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa6752hs_state, sd); +} + +/* ---------------------------------------------------------------------- */ + +static u8 PAT[] = { + 0xc2, /* i2c register */ + 0x00, /* table number for encoder */ + + 0x47, /* sync */ + 0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid(0) */ + 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ + + 0x00, /* PSI pointer to start of table */ + + 0x00, /* tid(0) */ + 0xb0, 0x0d, /* section_syntax_indicator(1), section_length(13) */ + + 0x00, 0x01, /* transport_stream_id(1) */ + + 0xc1, /* version_number(0), current_next_indicator(1) */ + + 0x00, 0x00, /* section_number(0), last_section_number(0) */ + + 0x00, 0x01, /* program_number(1) */ + + 0xe0, 0x00, /* PMT PID */ + + 0x00, 0x00, 0x00, 0x00 /* CRC32 */ +}; + +static u8 PMT[] = { + 0xc2, /* i2c register */ + 0x01, /* table number for encoder */ + + 0x47, /* sync */ + 0x40, 0x00, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0), pid */ + 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ + + 0x00, /* PSI pointer to start of table */ + + 0x02, /* tid(2) */ + 0xb0, 0x17, /* section_syntax_indicator(1), section_length(23) */ + + 0x00, 0x01, /* program_number(1) */ + + 0xc1, /* version_number(0), current_next_indicator(1) */ + + 0x00, 0x00, /* section_number(0), last_section_number(0) */ + + 0xe0, 0x00, /* PCR_PID */ + + 0xf0, 0x00, /* program_info_length(0) */ + + 0x02, 0xe0, 0x00, 0xf0, 0x00, /* video stream type(2), pid */ + 0x04, 0xe0, 0x00, 0xf0, 0x00, /* audio stream type(4), pid */ + + 0x00, 0x00, 0x00, 0x00 /* CRC32 */ +}; + +static u8 PMT_AC3[] = { + 0xc2, /* i2c register */ + 0x01, /* table number for encoder(1) */ + 0x47, /* sync */ + + 0x40, /* transport_error_indicator(0), payload_unit_start(1), transport_priority(0) */ + 0x10, /* PMT PID (0x0010) */ + 0x10, /* transport_scrambling_control(00), adaptation_field_control(01), continuity_counter(0) */ + + 0x00, /* PSI pointer to start of table */ + + 0x02, /* TID (2) */ + 0xb0, 0x1a, /* section_syntax_indicator(1), section_length(26) */ + + 0x00, 0x01, /* program_number(1) */ + + 0xc1, /* version_number(0), current_next_indicator(1) */ + + 0x00, 0x00, /* section_number(0), last_section_number(0) */ + + 0xe1, 0x04, /* PCR_PID (0x0104) */ + + 0xf0, 0x00, /* program_info_length(0) */ + + 0x02, 0xe1, 0x00, 0xf0, 0x00, /* video stream type(2), pid */ + 0x06, 0xe1, 0x03, 0xf0, 0x03, /* audio stream type(6), pid */ + 0x6a, /* AC3 */ + 0x01, /* Descriptor_length(1) */ + 0x00, /* component_type_flag(0), bsid_flag(0), mainid_flag(0), asvc_flag(0), reserved flags(0) */ + + 0xED, 0xDE, 0x2D, 0xF3 /* CRC32 BE */ +}; + +static struct saa6752hs_mpeg_params param_defaults = +{ + .ts_pid_pmt = 16, + .ts_pid_video = 260, + .ts_pid_audio = 256, + .ts_pid_pcr = 259, + + .vi_aspect = V4L2_MPEG_VIDEO_ASPECT_4x3, + .vi_bitrate = 4000, + .vi_bitrate_peak = 6000, + .vi_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + + .au_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + .au_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_256K, + .au_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_256K, +}; + +/* ---------------------------------------------------------------------- */ + +static int saa6752hs_chip_command(struct i2c_client *client, + enum saa6752hs_command command) +{ + unsigned char buf[3]; + unsigned long timeout; + int status = 0; + + /* execute the command */ + switch(command) { + case SAA6752HS_COMMAND_RESET: + buf[0] = 0x00; + break; + + case SAA6752HS_COMMAND_STOP: + buf[0] = 0x03; + break; + + case SAA6752HS_COMMAND_START: + buf[0] = 0x02; + break; + + case SAA6752HS_COMMAND_PAUSE: + buf[0] = 0x04; + break; + + case SAA6752HS_COMMAND_RECONFIGURE: + buf[0] = 0x05; + break; + + case SAA6752HS_COMMAND_SLEEP: + buf[0] = 0x06; + break; + + case SAA6752HS_COMMAND_RECONFIGURE_FORCE: + buf[0] = 0x07; + break; + + default: + return -EINVAL; + } + + /* set it and wait for it to be so */ + i2c_master_send(client, buf, 1); + timeout = jiffies + HZ * 3; + for (;;) { + /* get the current status */ + buf[0] = 0x10; + i2c_master_send(client, buf, 1); + i2c_master_recv(client, buf, 1); + + if (!(buf[0] & 0x20)) + break; + if (time_after(jiffies,timeout)) { + status = -ETIMEDOUT; + break; + } + + msleep(10); + } + + /* delay a bit to let encoder settle */ + msleep(50); + + return status; +} + + +static inline void set_reg8(struct i2c_client *client, uint8_t reg, uint8_t val) +{ + u8 buf[2]; + + buf[0] = reg; + buf[1] = val; + i2c_master_send(client, buf, 2); +} + +static inline void set_reg16(struct i2c_client *client, uint8_t reg, uint16_t val) +{ + u8 buf[3]; + + buf[0] = reg; + buf[1] = val >> 8; + buf[2] = val & 0xff; + i2c_master_send(client, buf, 3); +} + +static int saa6752hs_set_bitrate(struct i2c_client *client, + struct saa6752hs_state *h) +{ + struct saa6752hs_mpeg_params *params = &h->params; + int tot_bitrate; + int is_384k; + + /* set the bitrate mode */ + set_reg8(client, 0x71, + params->vi_bitrate_mode != V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + + /* set the video bitrate */ + if (params->vi_bitrate_mode == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) { + /* set the target bitrate */ + set_reg16(client, 0x80, params->vi_bitrate); + + /* set the max bitrate */ + set_reg16(client, 0x81, params->vi_bitrate_peak); + tot_bitrate = params->vi_bitrate_peak; + } else { + /* set the target bitrate (no max bitrate for CBR) */ + set_reg16(client, 0x81, params->vi_bitrate); + tot_bitrate = params->vi_bitrate; + } + + /* set the audio encoding */ + set_reg8(client, 0x93, + params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3); + + /* set the audio bitrate */ + if (params->au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) + is_384k = V4L2_MPEG_AUDIO_AC3_BITRATE_384K == params->au_ac3_bitrate; + else + is_384k = V4L2_MPEG_AUDIO_L2_BITRATE_384K == params->au_l2_bitrate; + set_reg8(client, 0x94, is_384k); + tot_bitrate += is_384k ? 384 : 256; + + /* Note: the total max bitrate is determined by adding the video and audio + bitrates together and also adding an extra 768kbit/s to stay on the + safe side. If more control should be required, then an extra MPEG control + should be added. */ + tot_bitrate += 768; + if (tot_bitrate > MPEG_TOTAL_TARGET_BITRATE_MAX) + tot_bitrate = MPEG_TOTAL_TARGET_BITRATE_MAX; + + /* set the total bitrate */ + set_reg16(client, 0xb1, tot_bitrate); + return 0; +} + + +static int get_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params, + struct v4l2_ext_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_STREAM_TYPE: + ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; + break; + case V4L2_CID_MPEG_STREAM_PID_PMT: + ctrl->value = params->ts_pid_pmt; + break; + case V4L2_CID_MPEG_STREAM_PID_AUDIO: + ctrl->value = params->ts_pid_audio; + break; + case V4L2_CID_MPEG_STREAM_PID_VIDEO: + ctrl->value = params->ts_pid_video; + break; + case V4L2_CID_MPEG_STREAM_PID_PCR: + ctrl->value = params->ts_pid_pcr; + break; + case V4L2_CID_MPEG_AUDIO_ENCODING: + ctrl->value = params->au_encoding; + break; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + ctrl->value = params->au_l2_bitrate; + break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (!has_ac3) + return -EINVAL; + ctrl->value = params->au_ac3_bitrate; + break; + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + ctrl->value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + ctrl->value = params->vi_aspect; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctrl->value = params->vi_bitrate * 1000; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + ctrl->value = params->vi_bitrate_peak * 1000; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + ctrl->value = params->vi_bitrate_mode; + break; + default: + return -EINVAL; + } + return 0; +} + +static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params, + struct v4l2_ext_control *ctrl, int set) +{ + int old = 0, new; + + new = ctrl->value; + switch (ctrl->id) { + case V4L2_CID_MPEG_STREAM_TYPE: + old = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; + if (set && new != old) + return -ERANGE; + new = old; + break; + case V4L2_CID_MPEG_STREAM_PID_PMT: + old = params->ts_pid_pmt; + if (set && new > MPEG_PID_MAX) + return -ERANGE; + if (new > MPEG_PID_MAX) + new = MPEG_PID_MAX; + params->ts_pid_pmt = new; + break; + case V4L2_CID_MPEG_STREAM_PID_AUDIO: + old = params->ts_pid_audio; + if (set && new > MPEG_PID_MAX) + return -ERANGE; + if (new > MPEG_PID_MAX) + new = MPEG_PID_MAX; + params->ts_pid_audio = new; + break; + case V4L2_CID_MPEG_STREAM_PID_VIDEO: + old = params->ts_pid_video; + if (set && new > MPEG_PID_MAX) + return -ERANGE; + if (new > MPEG_PID_MAX) + new = MPEG_PID_MAX; + params->ts_pid_video = new; + break; + case V4L2_CID_MPEG_STREAM_PID_PCR: + old = params->ts_pid_pcr; + if (set && new > MPEG_PID_MAX) + return -ERANGE; + if (new > MPEG_PID_MAX) + new = MPEG_PID_MAX; + params->ts_pid_pcr = new; + break; + case V4L2_CID_MPEG_AUDIO_ENCODING: + old = params->au_encoding; + if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 && + (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3)) + return -ERANGE; + params->au_encoding = new; + break; + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + old = params->au_l2_bitrate; + if (set && new != V4L2_MPEG_AUDIO_L2_BITRATE_256K && + new != V4L2_MPEG_AUDIO_L2_BITRATE_384K) + return -ERANGE; + if (new <= V4L2_MPEG_AUDIO_L2_BITRATE_256K) + new = V4L2_MPEG_AUDIO_L2_BITRATE_256K; + else + new = V4L2_MPEG_AUDIO_L2_BITRATE_384K; + params->au_l2_bitrate = new; + break; + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (!has_ac3) + return -EINVAL; + old = params->au_ac3_bitrate; + if (set && new != V4L2_MPEG_AUDIO_AC3_BITRATE_256K && + new != V4L2_MPEG_AUDIO_AC3_BITRATE_384K) + return -ERANGE; + if (new <= V4L2_MPEG_AUDIO_AC3_BITRATE_256K) + new = V4L2_MPEG_AUDIO_AC3_BITRATE_256K; + else + new = V4L2_MPEG_AUDIO_AC3_BITRATE_384K; + params->au_ac3_bitrate = new; + break; + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000; + if (set && new != old) + return -ERANGE; + new = old; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + old = V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + if (set && new != old) + return -ERANGE; + new = old; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + old = params->vi_aspect; + if (set && new != V4L2_MPEG_VIDEO_ASPECT_16x9 && + new != V4L2_MPEG_VIDEO_ASPECT_4x3) + return -ERANGE; + if (new != V4L2_MPEG_VIDEO_ASPECT_16x9) + new = V4L2_MPEG_VIDEO_ASPECT_4x3; + params->vi_aspect = new; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + old = params->vi_bitrate * 1000; + new = 1000 * (new / 1000); + if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) + return -ERANGE; + if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) + new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000; + params->vi_bitrate = new / 1000; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + old = params->vi_bitrate_peak * 1000; + new = 1000 * (new / 1000); + if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) + return -ERANGE; + if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000) + new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000; + params->vi_bitrate_peak = new / 1000; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + old = params->vi_bitrate_mode; + params->vi_bitrate_mode = new; + break; + default: + return -EINVAL; + } + ctrl->value = new; + return 0; +} + + +static int saa6752hs_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl) +{ + struct saa6752hs_state *h = to_state(sd); + struct saa6752hs_mpeg_params *params = &h->params; + int err; + + switch (qctrl->id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + h->has_ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 : + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + 1, V4L2_MPEG_AUDIO_ENCODING_LAYER_2); + + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_L2_BITRATE_256K, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1, + V4L2_MPEG_AUDIO_L2_BITRATE_256K); + + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (!h->has_ac3) + return -EINVAL; + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_AC3_BITRATE_256K, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K, 1, + V4L2_MPEG_AUDIO_AC3_BITRATE_256K); + + case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, 1, + V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000); + + case V4L2_CID_MPEG_VIDEO_ENCODING: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + + case V4L2_CID_MPEG_VIDEO_ASPECT: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_ASPECT_4x3, + V4L2_MPEG_VIDEO_ASPECT_16x9, 1, + V4L2_MPEG_VIDEO_ASPECT_4x3); + + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); + if (err == 0 && + params->vi_bitrate_mode == + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR) + qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + + case V4L2_CID_MPEG_STREAM_TYPE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_STREAM_TYPE_MPEG2_TS, + V4L2_MPEG_STREAM_TYPE_MPEG2_TS, 1, + V4L2_MPEG_STREAM_TYPE_MPEG2_TS); + + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + return v4l2_ctrl_query_fill(qctrl, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR); + case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000); + case V4L2_CID_MPEG_STREAM_PID_PMT: + return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 16); + case V4L2_CID_MPEG_STREAM_PID_AUDIO: + return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 260); + case V4L2_CID_MPEG_STREAM_PID_VIDEO: + return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 256); + case V4L2_CID_MPEG_STREAM_PID_PCR: + return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 259); + + default: + break; + } + return -EINVAL; +} + +static int saa6752hs_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qmenu) +{ + static const u32 mpeg_audio_encoding[] = { + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_CTRL_MENU_IDS_END + }; + static const u32 mpeg_audio_ac3_encoding[] = { + V4L2_MPEG_AUDIO_ENCODING_LAYER_2, + V4L2_MPEG_AUDIO_ENCODING_AC3, + V4L2_CTRL_MENU_IDS_END + }; + static u32 mpeg_audio_l2_bitrate[] = { + V4L2_MPEG_AUDIO_L2_BITRATE_256K, + V4L2_MPEG_AUDIO_L2_BITRATE_384K, + V4L2_CTRL_MENU_IDS_END + }; + static u32 mpeg_audio_ac3_bitrate[] = { + V4L2_MPEG_AUDIO_AC3_BITRATE_256K, + V4L2_MPEG_AUDIO_AC3_BITRATE_384K, + V4L2_CTRL_MENU_IDS_END + }; + struct saa6752hs_state *h = to_state(sd); + struct v4l2_queryctrl qctrl; + int err; + + qctrl.id = qmenu->id; + err = saa6752hs_queryctrl(sd, &qctrl); + if (err) + return err; + switch (qmenu->id) { + case V4L2_CID_MPEG_AUDIO_L2_BITRATE: + return v4l2_ctrl_query_menu_valid_items(qmenu, + mpeg_audio_l2_bitrate); + case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: + if (!h->has_ac3) + return -EINVAL; + return v4l2_ctrl_query_menu_valid_items(qmenu, + mpeg_audio_ac3_bitrate); + case V4L2_CID_MPEG_AUDIO_ENCODING: + return v4l2_ctrl_query_menu_valid_items(qmenu, + h->has_ac3 ? mpeg_audio_ac3_encoding : + mpeg_audio_encoding); + } + return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL); +} + +static int saa6752hs_init(struct v4l2_subdev *sd, u32 leading_null_bytes) +{ + unsigned char buf[9], buf2[4]; + struct saa6752hs_state *h = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned size; + u32 crc; + unsigned char localPAT[256]; + unsigned char localPMT[256]; + + /* Set video format - must be done first as it resets other settings */ + set_reg8(client, 0x41, h->video_format); + + /* Set number of lines in input signal */ + set_reg8(client, 0x40, (h->standard & V4L2_STD_525_60) ? 1 : 0); + + /* set bitrate */ + saa6752hs_set_bitrate(client, h); + + /* Set GOP structure {3, 13} */ + set_reg16(client, 0x72, 0x030d); + + /* Set minimum Q-scale {4} */ + set_reg8(client, 0x82, 0x04); + + /* Set maximum Q-scale {12} */ + set_reg8(client, 0x83, 0x0c); + + /* Set Output Protocol */ + set_reg8(client, 0xd0, 0x81); + + /* Set video output stream format {TS} */ + set_reg8(client, 0xb0, 0x05); + + /* Set leading null byte for TS */ + set_reg16(client, 0xf6, leading_null_bytes); + + /* compute PAT */ + memcpy(localPAT, PAT, sizeof(PAT)); + localPAT[17] = 0xe0 | ((h->params.ts_pid_pmt >> 8) & 0x0f); + localPAT[18] = h->params.ts_pid_pmt & 0xff; + crc = crc32_be(~0, &localPAT[7], sizeof(PAT) - 7 - 4); + localPAT[sizeof(PAT) - 4] = (crc >> 24) & 0xFF; + localPAT[sizeof(PAT) - 3] = (crc >> 16) & 0xFF; + localPAT[sizeof(PAT) - 2] = (crc >> 8) & 0xFF; + localPAT[sizeof(PAT) - 1] = crc & 0xFF; + + /* compute PMT */ + if (h->params.au_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) { + size = sizeof(PMT_AC3); + memcpy(localPMT, PMT_AC3, size); + } else { + size = sizeof(PMT); + memcpy(localPMT, PMT, size); + } + localPMT[3] = 0x40 | ((h->params.ts_pid_pmt >> 8) & 0x0f); + localPMT[4] = h->params.ts_pid_pmt & 0xff; + localPMT[15] = 0xE0 | ((h->params.ts_pid_pcr >> 8) & 0x0F); + localPMT[16] = h->params.ts_pid_pcr & 0xFF; + localPMT[20] = 0xE0 | ((h->params.ts_pid_video >> 8) & 0x0F); + localPMT[21] = h->params.ts_pid_video & 0xFF; + localPMT[25] = 0xE0 | ((h->params.ts_pid_audio >> 8) & 0x0F); + localPMT[26] = h->params.ts_pid_audio & 0xFF; + crc = crc32_be(~0, &localPMT[7], size - 7 - 4); + localPMT[size - 4] = (crc >> 24) & 0xFF; + localPMT[size - 3] = (crc >> 16) & 0xFF; + localPMT[size - 2] = (crc >> 8) & 0xFF; + localPMT[size - 1] = crc & 0xFF; + + /* Set Audio PID */ + set_reg16(client, 0xc1, h->params.ts_pid_audio); + + /* Set Video PID */ + set_reg16(client, 0xc0, h->params.ts_pid_video); + + /* Set PCR PID */ + set_reg16(client, 0xc4, h->params.ts_pid_pcr); + + /* Send SI tables */ + i2c_master_send(client, localPAT, sizeof(PAT)); + i2c_master_send(client, localPMT, size); + + /* mute then unmute audio. This removes buzzing artefacts */ + set_reg8(client, 0xa4, 1); + set_reg8(client, 0xa4, 0); + + /* start it going */ + saa6752hs_chip_command(client, SAA6752HS_COMMAND_START); + + /* readout current state */ + buf[0] = 0xE1; + buf[1] = 0xA7; + buf[2] = 0xFE; + buf[3] = 0x82; + buf[4] = 0xB0; + i2c_master_send(client, buf, 5); + i2c_master_recv(client, buf2, 4); + + /* change aspect ratio */ + buf[0] = 0xE0; + buf[1] = 0xA7; + buf[2] = 0xFE; + buf[3] = 0x82; + buf[4] = 0xB0; + buf[5] = buf2[0]; + switch (h->params.vi_aspect) { + case V4L2_MPEG_VIDEO_ASPECT_16x9: + buf[6] = buf2[1] | 0x40; + break; + case V4L2_MPEG_VIDEO_ASPECT_4x3: + default: + buf[6] = buf2[1] & 0xBF; + break; + } + buf[7] = buf2[2]; + buf[8] = buf2[3]; + i2c_master_send(client, buf, 9); + + return 0; +} + +static int saa6752hs_do_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls, int set) +{ + struct saa6752hs_state *h = to_state(sd); + struct saa6752hs_mpeg_params params; + int i; + + if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + + params = h->params; + for (i = 0; i < ctrls->count; i++) { + int err = handle_ctrl(h->has_ac3, ¶ms, ctrls->controls + i, set); + + if (err) { + ctrls->error_idx = i; + return err; + } + } + if (set) + h->params = params; + return 0; +} + +static int saa6752hs_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) +{ + return saa6752hs_do_ext_ctrls(sd, ctrls, 1); +} + +static int saa6752hs_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) +{ + return saa6752hs_do_ext_ctrls(sd, ctrls, 0); +} + +static int saa6752hs_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls) +{ + struct saa6752hs_state *h = to_state(sd); + int i; + + if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + int err = get_ctrl(h->has_ac3, &h->params, ctrls->controls + i); + + if (err) { + ctrls->error_idx = i; + return err; + } + } + return 0; +} + +static int saa6752hs_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +{ + struct saa6752hs_state *h = to_state(sd); + + if (h->video_format == SAA6752HS_VF_UNKNOWN) + h->video_format = SAA6752HS_VF_D1; + f->width = v4l2_format_table[h->video_format].fmt.pix.width; + f->height = v4l2_format_table[h->video_format].fmt.pix.height; + f->code = V4L2_MBUS_FMT_FIXED; + f->field = V4L2_FIELD_INTERLACED; + f->colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +static int saa6752hs_s_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) +{ + struct saa6752hs_state *h = to_state(sd); + int dist_352, dist_480, dist_720; + + if (f->code != V4L2_MBUS_FMT_FIXED) + return -EINVAL; + + /* + FIXME: translate and round width/height into EMPRESS + subsample type: + + type | PAL | NTSC + --------------------------- + SIF | 352x288 | 352x240 + 1/2 D1 | 352x576 | 352x480 + 2/3 D1 | 480x576 | 480x480 + D1 | 720x576 | 720x480 + */ + + dist_352 = abs(f->width - 352); + dist_480 = abs(f->width - 480); + dist_720 = abs(f->width - 720); + if (dist_720 < dist_480) { + f->width = 720; + f->height = 576; + h->video_format = SAA6752HS_VF_D1; + } else if (dist_480 < dist_352) { + f->width = 480; + f->height = 576; + h->video_format = SAA6752HS_VF_2_3_D1; + } else { + f->width = 352; + if (abs(f->height - 576) < + abs(f->height - 288)) { + f->height = 576; + h->video_format = SAA6752HS_VF_1_2_D1; + } else { + f->height = 288; + h->video_format = SAA6752HS_VF_SIF; + } + } + f->field = V4L2_FIELD_INTERLACED; + f->colorspace = V4L2_COLORSPACE_SMPTE170M; + return 0; +} + +static int saa6752hs_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct saa6752hs_state *h = to_state(sd); + + h->standard = std; + return 0; +} + +static int saa6752hs_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct saa6752hs_state *h = to_state(sd); + + return v4l2_chip_ident_i2c_client(client, + chip, h->chip, h->revision); +} + +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops saa6752hs_core_ops = { + .g_chip_ident = saa6752hs_g_chip_ident, + .init = saa6752hs_init, + .queryctrl = saa6752hs_queryctrl, + .querymenu = saa6752hs_querymenu, + .g_ext_ctrls = saa6752hs_g_ext_ctrls, + .s_ext_ctrls = saa6752hs_s_ext_ctrls, + .try_ext_ctrls = saa6752hs_try_ext_ctrls, + .s_std = saa6752hs_s_std, +}; + +static const struct v4l2_subdev_video_ops saa6752hs_video_ops = { + .s_mbus_fmt = saa6752hs_s_mbus_fmt, + .g_mbus_fmt = saa6752hs_g_mbus_fmt, +}; + +static const struct v4l2_subdev_ops saa6752hs_ops = { + .core = &saa6752hs_core_ops, + .video = &saa6752hs_video_ops, +}; + +static int saa6752hs_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL); + struct v4l2_subdev *sd; + u8 addr = 0x13; + u8 data[12]; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + if (h == NULL) + return -ENOMEM; + sd = &h->sd; + v4l2_i2c_subdev_init(sd, client, &saa6752hs_ops); + + i2c_master_send(client, &addr, 1); + i2c_master_recv(client, data, sizeof(data)); + h->chip = V4L2_IDENT_SAA6752HS; + h->revision = (data[8] << 8) | data[9]; + h->has_ac3 = 0; + if (h->revision == 0x0206) { + h->chip = V4L2_IDENT_SAA6752HS_AC3; + h->has_ac3 = 1; + v4l_info(client, "support AC-3\n"); + } + h->params = param_defaults; + h->standard = 0; /* Assume 625 input lines */ + return 0; +} + +static int saa6752hs_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id saa6752hs_id[] = { + { "saa6752hs", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, saa6752hs_id); + +static struct i2c_driver saa6752hs_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "saa6752hs", + }, + .probe = saa6752hs_probe, + .remove = saa6752hs_remove, + .id_table = saa6752hs_id, +}; + +module_i2c_driver(saa6752hs_driver); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/pci/saa7134/saa7134-alsa.c b/drivers/media/pci/saa7134/saa7134-alsa.c new file mode 100644 index 000000000000..10460fd3ce39 --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-alsa.c @@ -0,0 +1,1209 @@ +/* + * SAA713x ALSA support for V4L + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/wait.h> +#include <linux/module.h> +#include <sound/core.h> +#include <sound/control.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/initval.h> +#include <linux/interrupt.h> + +#include "saa7134.h" +#include "saa7134-reg.h" + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug,"enable debug messages [alsa]"); + +/* + * Configuration macros + */ + +/* defaults */ +#define MIXER_ADDR_UNSELECTED -1 +#define MIXER_ADDR_TVTUNER 0 +#define MIXER_ADDR_LINE1 1 +#define MIXER_ADDR_LINE2 2 +#define MIXER_ADDR_LAST 2 + + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; + +module_param_array(index, int, NULL, 0444); +module_param_array(enable, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for SAA7134 capture interface(s)."); +MODULE_PARM_DESC(enable, "Enable (or not) the SAA7134 capture interface(s)."); + +#define dprintk(fmt, arg...) if (debug) \ + printk(KERN_DEBUG "%s/alsa: " fmt, dev->name , ##arg) + + + +/* + * Main chip structure + */ + +typedef struct snd_card_saa7134 { + struct snd_card *card; + spinlock_t mixer_lock; + int mixer_volume[MIXER_ADDR_LAST+1][2]; + int capture_source_addr; + int capture_source[2]; + struct snd_kcontrol *capture_ctl[MIXER_ADDR_LAST+1]; + struct pci_dev *pci; + struct saa7134_dev *dev; + + unsigned long iobase; + s16 irq; + u16 mute_was_on; + + spinlock_t lock; +} snd_card_saa7134_t; + + +/* + * PCM structure + */ + +typedef struct snd_card_saa7134_pcm { + struct saa7134_dev *dev; + + spinlock_t lock; + + struct snd_pcm_substream *substream; +} snd_card_saa7134_pcm_t; + +static struct snd_card *snd_saa7134_cards[SNDRV_CARDS]; + + +/* + * saa7134 DMA audio stop + * + * Called when the capture device is released or the buffer overflows + * + * - Copied verbatim from saa7134-oss's dsp_dma_stop. + * + */ + +static void saa7134_dma_stop(struct saa7134_dev *dev) +{ + dev->dmasound.dma_blk = -1; + dev->dmasound.dma_running = 0; + saa7134_set_dmabits(dev); +} + +/* + * saa7134 DMA audio start + * + * Called when preparing the capture device for use + * + * - Copied verbatim from saa7134-oss's dsp_dma_start. + * + */ + +static void saa7134_dma_start(struct saa7134_dev *dev) +{ + dev->dmasound.dma_blk = 0; + dev->dmasound.dma_running = 1; + saa7134_set_dmabits(dev); +} + +/* + * saa7134 audio DMA IRQ handler + * + * Called whenever we get an SAA7134_IRQ_REPORT_DONE_RA3 interrupt + * Handles shifting between the 2 buffers, manages the read counters, + * and notifies ALSA when periods elapse + * + * - Mostly copied from saa7134-oss's saa7134_irq_oss_done. + * + */ + +static void saa7134_irq_alsa_done(struct saa7134_dev *dev, + unsigned long status) +{ + int next_blk, reg = 0; + + spin_lock(&dev->slock); + if (UNSET == dev->dmasound.dma_blk) { + dprintk("irq: recording stopped\n"); + goto done; + } + if (0 != (status & 0x0f000000)) + dprintk("irq: lost %ld\n", (status >> 24) & 0x0f); + if (0 == (status & 0x10000000)) { + /* odd */ + if (0 == (dev->dmasound.dma_blk & 0x01)) + reg = SAA7134_RS_BA1(6); + } else { + /* even */ + if (1 == (dev->dmasound.dma_blk & 0x01)) + reg = SAA7134_RS_BA2(6); + } + if (0 == reg) { + dprintk("irq: field oops [%s]\n", + (status & 0x10000000) ? "even" : "odd"); + goto done; + } + + if (dev->dmasound.read_count >= dev->dmasound.blksize * (dev->dmasound.blocks-2)) { + dprintk("irq: overrun [full=%d/%d] - Blocks in %d\n",dev->dmasound.read_count, + dev->dmasound.bufsize, dev->dmasound.blocks); + spin_unlock(&dev->slock); + snd_pcm_stop(dev->dmasound.substream,SNDRV_PCM_STATE_XRUN); + return; + } + + /* next block addr */ + next_blk = (dev->dmasound.dma_blk + 2) % dev->dmasound.blocks; + saa_writel(reg,next_blk * dev->dmasound.blksize); + if (debug > 2) + dprintk("irq: ok, %s, next_blk=%d, addr=%x, blocks=%u, size=%u, read=%u\n", + (status & 0x10000000) ? "even" : "odd ", next_blk, + next_blk * dev->dmasound.blksize, dev->dmasound.blocks, dev->dmasound.blksize, dev->dmasound.read_count); + + /* update status & wake waiting readers */ + dev->dmasound.dma_blk = (dev->dmasound.dma_blk + 1) % dev->dmasound.blocks; + dev->dmasound.read_count += dev->dmasound.blksize; + + dev->dmasound.recording_on = reg; + + if (dev->dmasound.read_count >= snd_pcm_lib_period_bytes(dev->dmasound.substream)) { + spin_unlock(&dev->slock); + snd_pcm_period_elapsed(dev->dmasound.substream); + spin_lock(&dev->slock); + } + + done: + spin_unlock(&dev->slock); + +} + +/* + * IRQ request handler + * + * Runs along with saa7134's IRQ handler, discards anything that isn't + * DMA sound + * + */ + +static irqreturn_t saa7134_alsa_irq(int irq, void *dev_id) +{ + struct saa7134_dmasound *dmasound = dev_id; + struct saa7134_dev *dev = dmasound->priv_data; + + unsigned long report, status; + int loop, handled = 0; + + for (loop = 0; loop < 10; loop++) { + report = saa_readl(SAA7134_IRQ_REPORT); + status = saa_readl(SAA7134_IRQ_STATUS); + + if (report & SAA7134_IRQ_REPORT_DONE_RA3) { + handled = 1; + saa_writel(SAA7134_IRQ_REPORT, + SAA7134_IRQ_REPORT_DONE_RA3); + saa7134_irq_alsa_done(dev, status); + } else { + goto out; + } + } + + if (loop == 10) { + dprintk("error! looping IRQ!"); + } + +out: + return IRQ_RETVAL(handled); +} + +/* + * ALSA capture trigger + * + * - One of the ALSA capture callbacks. + * + * Called whenever a capture is started or stopped. Must be defined, + * but there's nothing we want to do here + * + */ + +static int snd_card_saa7134_capture_trigger(struct snd_pcm_substream * substream, + int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + snd_card_saa7134_pcm_t *pcm = runtime->private_data; + struct saa7134_dev *dev=pcm->dev; + int err = 0; + + spin_lock(&dev->slock); + if (cmd == SNDRV_PCM_TRIGGER_START) { + /* start dma */ + saa7134_dma_start(dev); + } else if (cmd == SNDRV_PCM_TRIGGER_STOP) { + /* stop dma */ + saa7134_dma_stop(dev); + } else { + err = -EINVAL; + } + spin_unlock(&dev->slock); + + return err; +} + +/* + * DMA buffer initialization + * + * Uses V4L functions to initialize the DMA. Shouldn't be necessary in + * ALSA, but I was unable to use ALSA's own DMA, and had to force the + * usage of V4L's + * + * - Copied verbatim from saa7134-oss. + * + */ + +static int dsp_buffer_init(struct saa7134_dev *dev) +{ + int err; + + BUG_ON(!dev->dmasound.bufsize); + + videobuf_dma_init(&dev->dmasound.dma); + err = videobuf_dma_init_kernel(&dev->dmasound.dma, PCI_DMA_FROMDEVICE, + (dev->dmasound.bufsize + PAGE_SIZE) >> PAGE_SHIFT); + if (0 != err) + return err; + return 0; +} + +/* + * DMA buffer release + * + * Called after closing the device, during snd_card_saa7134_capture_close + * + */ + +static int dsp_buffer_free(struct saa7134_dev *dev) +{ + BUG_ON(!dev->dmasound.blksize); + + videobuf_dma_free(&dev->dmasound.dma); + + dev->dmasound.blocks = 0; + dev->dmasound.blksize = 0; + dev->dmasound.bufsize = 0; + + return 0; +} + +/* + * Setting the capture source and updating the ALSA controls + */ +static int snd_saa7134_capsrc_set(struct snd_kcontrol *kcontrol, + int left, int right, bool force_notify) +{ + snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); + int change = 0, addr = kcontrol->private_value; + int active, old_addr; + u32 anabar, xbarin; + int analog_io, rate; + struct saa7134_dev *dev; + + dev = chip->dev; + + spin_lock_irq(&chip->mixer_lock); + + active = left != 0 || right != 0; + old_addr = chip->capture_source_addr; + + /* The active capture source cannot be deactivated */ + if (active) { + change = old_addr != addr || + chip->capture_source[0] != left || + chip->capture_source[1] != right; + + chip->capture_source[0] = left; + chip->capture_source[1] = right; + chip->capture_source_addr = addr; + dev->dmasound.input = addr; + } + spin_unlock_irq(&chip->mixer_lock); + + if (change) { + switch (dev->pci->device) { + + case PCI_DEVICE_ID_PHILIPS_SAA7134: + switch (addr) { + case MIXER_ADDR_TVTUNER: + saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, + 0xc0, 0xc0); + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, + 0x03, 0x00); + break; + case MIXER_ADDR_LINE1: + case MIXER_ADDR_LINE2: + analog_io = (MIXER_ADDR_LINE1 == addr) ? + 0x00 : 0x08; + rate = (32000 == dev->dmasound.rate) ? + 0x01 : 0x03; + saa_andorb(SAA7134_ANALOG_IO_SELECT, + 0x08, analog_io); + saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, + 0xc0, 0x80); + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, + 0x03, rate); + break; + } + + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + xbarin = 0x03; /* adc */ + anabar = 0; + switch (addr) { + case MIXER_ADDR_TVTUNER: + xbarin = 0; /* Demodulator */ + anabar = 2; /* DACs */ + break; + case MIXER_ADDR_LINE1: + anabar = 0; /* aux1, aux1 */ + break; + case MIXER_ADDR_LINE2: + anabar = 9; /* aux2, aux2 */ + break; + } + + /* output xbar always main channel */ + saa_dsp_writel(dev, SAA7133_DIGITAL_OUTPUT_SEL1, + 0xbbbb10); + + if (left || right) { + /* We've got data, turn the input on */ + saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, + xbarin); + saa_writel(SAA7133_ANALOG_IO_SELECT, anabar); + } else { + saa_dsp_writel(dev, SAA7133_DIGITAL_INPUT_XBAR1, + 0); + saa_writel(SAA7133_ANALOG_IO_SELECT, 0); + } + break; + } + } + + if (change) { + if (force_notify) + snd_ctl_notify(chip->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &chip->capture_ctl[addr]->id); + + if (old_addr != MIXER_ADDR_UNSELECTED && old_addr != addr) + snd_ctl_notify(chip->card, + SNDRV_CTL_EVENT_MASK_VALUE, + &chip->capture_ctl[old_addr]->id); + } + + return change; +} + +/* + * ALSA PCM preparation + * + * - One of the ALSA capture callbacks. + * + * Called right after the capture device is opened, this function configures + * the buffer using the previously defined functions, allocates the memory, + * sets up the hardware registers, and then starts the DMA. When this function + * returns, the audio should be flowing. + * + */ + +static int snd_card_saa7134_capture_prepare(struct snd_pcm_substream * substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int bswap, sign; + u32 fmt, control; + snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); + struct saa7134_dev *dev; + snd_card_saa7134_pcm_t *pcm = runtime->private_data; + + pcm->dev->dmasound.substream = substream; + + dev = saa7134->dev; + + if (snd_pcm_format_width(runtime->format) == 8) + fmt = 0x00; + else + fmt = 0x01; + + if (snd_pcm_format_signed(runtime->format)) + sign = 1; + else + sign = 0; + + if (snd_pcm_format_big_endian(runtime->format)) + bswap = 1; + else + bswap = 0; + + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + if (1 == runtime->channels) + fmt |= (1 << 3); + if (2 == runtime->channels) + fmt |= (3 << 3); + if (sign) + fmt |= 0x04; + + fmt |= (MIXER_ADDR_TVTUNER == dev->dmasound.input) ? 0xc0 : 0x80; + saa_writeb(SAA7134_NUM_SAMPLES0, ((dev->dmasound.blksize - 1) & 0x0000ff)); + saa_writeb(SAA7134_NUM_SAMPLES1, ((dev->dmasound.blksize - 1) & 0x00ff00) >> 8); + saa_writeb(SAA7134_NUM_SAMPLES2, ((dev->dmasound.blksize - 1) & 0xff0000) >> 16); + saa_writeb(SAA7134_AUDIO_FORMAT_CTRL, fmt); + + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + if (1 == runtime->channels) + fmt |= (1 << 4); + if (2 == runtime->channels) + fmt |= (2 << 4); + if (!sign) + fmt |= 0x04; + saa_writel(SAA7133_NUM_SAMPLES, dev->dmasound.blksize -1); + saa_writel(SAA7133_AUDIO_CHANNEL, 0x543210 | (fmt << 24)); + break; + } + + dprintk("rec_start: afmt=%d ch=%d => fmt=0x%x swap=%c\n", + runtime->format, runtime->channels, fmt, + bswap ? 'b' : '-'); + /* dma: setup channel 6 (= AUDIO) */ + control = SAA7134_RS_CONTROL_BURST_16 | + SAA7134_RS_CONTROL_ME | + (dev->dmasound.pt.dma >> 12); + if (bswap) + control |= SAA7134_RS_CONTROL_BSWAP; + + saa_writel(SAA7134_RS_BA1(6),0); + saa_writel(SAA7134_RS_BA2(6),dev->dmasound.blksize); + saa_writel(SAA7134_RS_PITCH(6),0); + saa_writel(SAA7134_RS_CONTROL(6),control); + + dev->dmasound.rate = runtime->rate; + + /* Setup and update the card/ALSA controls */ + snd_saa7134_capsrc_set(saa7134->capture_ctl[dev->dmasound.input], 1, 1, + true); + + return 0; + +} + +/* + * ALSA pointer fetching + * + * - One of the ALSA capture callbacks. + * + * Called whenever a period elapses, it must return the current hardware + * position of the buffer. + * Also resets the read counter used to prevent overruns + * + */ + +static snd_pcm_uframes_t +snd_card_saa7134_capture_pointer(struct snd_pcm_substream * substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + snd_card_saa7134_pcm_t *pcm = runtime->private_data; + struct saa7134_dev *dev=pcm->dev; + + if (dev->dmasound.read_count) { + dev->dmasound.read_count -= snd_pcm_lib_period_bytes(substream); + dev->dmasound.read_offset += snd_pcm_lib_period_bytes(substream); + if (dev->dmasound.read_offset == dev->dmasound.bufsize) + dev->dmasound.read_offset = 0; + } + + return bytes_to_frames(runtime, dev->dmasound.read_offset); +} + +/* + * ALSA hardware capabilities definition + * + * Report only 32kHz for ALSA: + * + * - SAA7133/35 uses DDEP (DemDec Easy Programming mode), which works in 32kHz + * only + * - SAA7134 for TV mode uses DemDec mode (32kHz) + * - Radio works in 32kHz only + * - When recording 48kHz from Line1/Line2, switching of capture source to TV + * means + * switching to 32kHz without any frequency translation + */ + +static struct snd_pcm_hardware snd_card_saa7134_capture = +{ + .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_U16_BE, + .rates = SNDRV_PCM_RATE_32000, + .rate_min = 32000, + .rate_max = 32000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = (256*1024), + .period_bytes_min = 64, + .period_bytes_max = (256*1024), + .periods_min = 4, + .periods_max = 1024, +}; + +static void snd_card_saa7134_runtime_free(struct snd_pcm_runtime *runtime) +{ + snd_card_saa7134_pcm_t *pcm = runtime->private_data; + + kfree(pcm); +} + + +/* + * ALSA hardware params + * + * - One of the ALSA capture callbacks. + * + * Called on initialization, right before the PCM preparation + * + */ + +static int snd_card_saa7134_hw_params(struct snd_pcm_substream * substream, + struct snd_pcm_hw_params * hw_params) +{ + snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); + struct saa7134_dev *dev; + unsigned int period_size, periods; + int err; + + period_size = params_period_bytes(hw_params); + periods = params_periods(hw_params); + + if (period_size < 0x100 || period_size > 0x10000) + return -EINVAL; + if (periods < 4) + return -EINVAL; + if (period_size * periods > 1024 * 1024) + return -EINVAL; + + dev = saa7134->dev; + + if (dev->dmasound.blocks == periods && + dev->dmasound.blksize == period_size) + return 0; + + /* release the old buffer */ + if (substream->runtime->dma_area) { + saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); + videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + dsp_buffer_free(dev); + substream->runtime->dma_area = NULL; + } + dev->dmasound.blocks = periods; + dev->dmasound.blksize = period_size; + dev->dmasound.bufsize = period_size * periods; + + err = dsp_buffer_init(dev); + if (0 != err) { + dev->dmasound.blocks = 0; + dev->dmasound.blksize = 0; + dev->dmasound.bufsize = 0; + return err; + } + + if (0 != (err = videobuf_dma_map(&dev->pci->dev, &dev->dmasound.dma))) { + dsp_buffer_free(dev); + return err; + } + if (0 != (err = saa7134_pgtable_alloc(dev->pci,&dev->dmasound.pt))) { + videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + dsp_buffer_free(dev); + return err; + } + if (0 != (err = saa7134_pgtable_build(dev->pci,&dev->dmasound.pt, + dev->dmasound.dma.sglist, + dev->dmasound.dma.sglen, + 0))) { + saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); + videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + dsp_buffer_free(dev); + return err; + } + + /* I should be able to use runtime->dma_addr in the control + byte, but it doesn't work. So I allocate the DMA using the + V4L functions, and force ALSA to use that as the DMA area */ + + substream->runtime->dma_area = dev->dmasound.dma.vaddr; + substream->runtime->dma_bytes = dev->dmasound.bufsize; + substream->runtime->dma_addr = 0; + + return 0; + +} + +/* + * ALSA hardware release + * + * - One of the ALSA capture callbacks. + * + * Called after closing the device, but before snd_card_saa7134_capture_close + * It stops the DMA audio and releases the buffers. + * + */ + +static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream) +{ + snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); + struct saa7134_dev *dev; + + dev = saa7134->dev; + + if (substream->runtime->dma_area) { + saa7134_pgtable_free(dev->pci, &dev->dmasound.pt); + videobuf_dma_unmap(&dev->pci->dev, &dev->dmasound.dma); + dsp_buffer_free(dev); + substream->runtime->dma_area = NULL; + } + + return 0; +} + +/* + * ALSA capture finish + * + * - One of the ALSA capture callbacks. + * + * Called after closing the device. + * + */ + +static int snd_card_saa7134_capture_close(struct snd_pcm_substream * substream) +{ + snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); + struct saa7134_dev *dev = saa7134->dev; + + if (saa7134->mute_was_on) { + dev->ctl_mute = 1; + saa7134_tvaudio_setmute(dev); + } + return 0; +} + +/* + * ALSA capture start + * + * - One of the ALSA capture callbacks. + * + * Called when opening the device. It creates and populates the PCM + * structure + * + */ + +static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + snd_card_saa7134_pcm_t *pcm; + snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); + struct saa7134_dev *dev; + int amux, err; + + if (!saa7134) { + printk(KERN_ERR "BUG: saa7134 can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + dev = saa7134->dev; + mutex_lock(&dev->dmasound.lock); + + dev->dmasound.read_count = 0; + dev->dmasound.read_offset = 0; + + amux = dev->input->amux; + if ((amux < 1) || (amux > 3)) + amux = 1; + dev->dmasound.input = amux - 1; + + mutex_unlock(&dev->dmasound.lock); + + pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); + if (pcm == NULL) + return -ENOMEM; + + pcm->dev=saa7134->dev; + + spin_lock_init(&pcm->lock); + + pcm->substream = substream; + runtime->private_data = pcm; + runtime->private_free = snd_card_saa7134_runtime_free; + runtime->hw = snd_card_saa7134_capture; + + if (dev->ctl_mute != 0) { + saa7134->mute_was_on = 1; + dev->ctl_mute = 0; + saa7134_tvaudio_setmute(dev); + } + + err = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + return err; + + err = snd_pcm_hw_constraint_step(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS, 2); + if (err < 0) + return err; + + return 0; +} + +/* + * page callback (needed for mmap) + */ + +static struct page *snd_card_saa7134_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + void *pageptr = substream->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); +} + +/* + * ALSA capture callbacks definition + */ + +static struct snd_pcm_ops snd_card_saa7134_capture_ops = { + .open = snd_card_saa7134_capture_open, + .close = snd_card_saa7134_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_card_saa7134_hw_params, + .hw_free = snd_card_saa7134_hw_free, + .prepare = snd_card_saa7134_capture_prepare, + .trigger = snd_card_saa7134_capture_trigger, + .pointer = snd_card_saa7134_capture_pointer, + .page = snd_card_saa7134_page, +}; + +/* + * ALSA PCM setup + * + * Called when initializing the board. Sets up the name and hooks up + * the callbacks + * + */ + +static int snd_card_saa7134_pcm(snd_card_saa7134_t *saa7134, int device) +{ + struct snd_pcm *pcm; + int err; + + if ((err = snd_pcm_new(saa7134->card, "SAA7134 PCM", device, 0, 1, &pcm)) < 0) + return err; + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_card_saa7134_capture_ops); + pcm->private_data = saa7134; + pcm->info_flags = 0; + strcpy(pcm->name, "SAA7134 PCM"); + return 0; +} + +#define SAA713x_VOLUME(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_saa7134_volume_info, \ + .get = snd_saa7134_volume_get, .put = snd_saa7134_volume_put, \ + .private_value = addr } + +static int snd_saa7134_volume_info(struct snd_kcontrol * kcontrol, + struct snd_ctl_elem_info * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 20; + return 0; +} + +static int snd_saa7134_volume_get(struct snd_kcontrol * kcontrol, + struct snd_ctl_elem_value * ucontrol) +{ + snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); + int addr = kcontrol->private_value; + + ucontrol->value.integer.value[0] = chip->mixer_volume[addr][0]; + ucontrol->value.integer.value[1] = chip->mixer_volume[addr][1]; + return 0; +} + +static int snd_saa7134_volume_put(struct snd_kcontrol * kcontrol, + struct snd_ctl_elem_value * ucontrol) +{ + snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); + struct saa7134_dev *dev = chip->dev; + + int change, addr = kcontrol->private_value; + int left, right; + + left = ucontrol->value.integer.value[0]; + if (left < 0) + left = 0; + if (left > 20) + left = 20; + right = ucontrol->value.integer.value[1]; + if (right < 0) + right = 0; + if (right > 20) + right = 20; + spin_lock_irq(&chip->mixer_lock); + change = 0; + if (chip->mixer_volume[addr][0] != left) { + change = 1; + right = left; + } + if (chip->mixer_volume[addr][1] != right) { + change = 1; + left = right; + } + if (change) { + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + switch (addr) { + case MIXER_ADDR_TVTUNER: + left = 20; + break; + case MIXER_ADDR_LINE1: + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x10, + (left > 10) ? 0x00 : 0x10); + break; + case MIXER_ADDR_LINE2: + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x20, + (left > 10) ? 0x00 : 0x20); + break; + } + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + switch (addr) { + case MIXER_ADDR_TVTUNER: + left = 20; + break; + case MIXER_ADDR_LINE1: + saa_andorb(0x0594, 0x10, + (left > 10) ? 0x00 : 0x10); + break; + case MIXER_ADDR_LINE2: + saa_andorb(0x0594, 0x20, + (left > 10) ? 0x00 : 0x20); + break; + } + break; + } + chip->mixer_volume[addr][0] = left; + chip->mixer_volume[addr][1] = right; + } + spin_unlock_irq(&chip->mixer_lock); + return change; +} + +#define SAA713x_CAPSRC(xname, xindex, addr) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_saa7134_capsrc_info, \ + .get = snd_saa7134_capsrc_get, .put = snd_saa7134_capsrc_put, \ + .private_value = addr } + +static int snd_saa7134_capsrc_info(struct snd_kcontrol * kcontrol, + struct snd_ctl_elem_info * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_saa7134_capsrc_get(struct snd_kcontrol * kcontrol, + struct snd_ctl_elem_value * ucontrol) +{ + snd_card_saa7134_t *chip = snd_kcontrol_chip(kcontrol); + int addr = kcontrol->private_value; + + spin_lock_irq(&chip->mixer_lock); + if (chip->capture_source_addr == addr) { + ucontrol->value.integer.value[0] = chip->capture_source[0]; + ucontrol->value.integer.value[1] = chip->capture_source[1]; + } else { + ucontrol->value.integer.value[0] = 0; + ucontrol->value.integer.value[1] = 0; + } + spin_unlock_irq(&chip->mixer_lock); + + return 0; +} + +static int snd_saa7134_capsrc_put(struct snd_kcontrol * kcontrol, + struct snd_ctl_elem_value * ucontrol) +{ + int left, right; + left = ucontrol->value.integer.value[0] & 1; + right = ucontrol->value.integer.value[1] & 1; + + return snd_saa7134_capsrc_set(kcontrol, left, right, false); +} + +static struct snd_kcontrol_new snd_saa7134_volume_controls[] = { +SAA713x_VOLUME("Video Volume", 0, MIXER_ADDR_TVTUNER), +SAA713x_VOLUME("Line Volume", 1, MIXER_ADDR_LINE1), +SAA713x_VOLUME("Line Volume", 2, MIXER_ADDR_LINE2), +}; + +static struct snd_kcontrol_new snd_saa7134_capture_controls[] = { +SAA713x_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER), +SAA713x_CAPSRC("Line Capture Switch", 1, MIXER_ADDR_LINE1), +SAA713x_CAPSRC("Line Capture Switch", 2, MIXER_ADDR_LINE2), +}; + +/* + * ALSA mixer setup + * + * Called when initializing the board. Sets up the name and hooks up + * the callbacks + * + */ + +static int snd_card_saa7134_new_mixer(snd_card_saa7134_t * chip) +{ + struct snd_card *card = chip->card; + struct snd_kcontrol *kcontrol; + unsigned int idx; + int err, addr; + + strcpy(card->mixername, "SAA7134 Mixer"); + + for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_volume_controls); idx++) { + kcontrol = snd_ctl_new1(&snd_saa7134_volume_controls[idx], + chip); + err = snd_ctl_add(card, kcontrol); + if (err < 0) + return err; + } + + for (idx = 0; idx < ARRAY_SIZE(snd_saa7134_capture_controls); idx++) { + kcontrol = snd_ctl_new1(&snd_saa7134_capture_controls[idx], + chip); + addr = snd_saa7134_capture_controls[idx].private_value; + chip->capture_ctl[addr] = kcontrol; + err = snd_ctl_add(card, kcontrol); + if (err < 0) + return err; + } + + chip->capture_source_addr = MIXER_ADDR_UNSELECTED; + return 0; +} + +static void snd_saa7134_free(struct snd_card * card) +{ + snd_card_saa7134_t *chip = card->private_data; + + if (chip->dev->dmasound.priv_data == NULL) + return; + + if (chip->irq >= 0) + free_irq(chip->irq, &chip->dev->dmasound); + + chip->dev->dmasound.priv_data = NULL; + +} + +/* + * ALSA initialization + * + * Called by the init routine, once for each saa7134 device present, + * it creates the basic structures and registers the ALSA devices + * + */ + +static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) +{ + + struct snd_card *card; + snd_card_saa7134_t *chip; + int err; + + + if (devnum >= SNDRV_CARDS) + return -ENODEV; + if (!enable[devnum]) + return -ENODEV; + + err = snd_card_create(index[devnum], id[devnum], THIS_MODULE, + sizeof(snd_card_saa7134_t), &card); + if (err < 0) + return err; + + strcpy(card->driver, "SAA7134"); + + /* Card "creation" */ + + card->private_free = snd_saa7134_free; + chip = card->private_data; + + spin_lock_init(&chip->lock); + spin_lock_init(&chip->mixer_lock); + + chip->dev = dev; + + chip->card = card; + + chip->pci = dev->pci; + chip->iobase = pci_resource_start(dev->pci, 0); + + + err = request_irq(dev->pci->irq, saa7134_alsa_irq, + IRQF_SHARED | IRQF_DISABLED, dev->name, + (void*) &dev->dmasound); + + if (err < 0) { + printk(KERN_ERR "%s: can't get IRQ %d for ALSA\n", + dev->name, dev->pci->irq); + goto __nodev; + } + + chip->irq = dev->pci->irq; + + mutex_init(&dev->dmasound.lock); + + if ((err = snd_card_saa7134_new_mixer(chip)) < 0) + goto __nodev; + + if ((err = snd_card_saa7134_pcm(chip, 0)) < 0) + goto __nodev; + + snd_card_set_dev(card, &chip->pci->dev); + + /* End of "creation" */ + + strcpy(card->shortname, "SAA7134"); + sprintf(card->longname, "%s at 0x%lx irq %d", + chip->dev->name, chip->iobase, chip->irq); + + printk(KERN_INFO "%s/alsa: %s registered as card %d\n",dev->name,card->longname,index[devnum]); + + if ((err = snd_card_register(card)) == 0) { + snd_saa7134_cards[devnum] = card; + return 0; + } + +__nodev: + snd_card_free(card); + return err; +} + + +static int alsa_device_init(struct saa7134_dev *dev) +{ + dev->dmasound.priv_data = dev; + alsa_card_saa7134_create(dev,dev->nr); + return 1; +} + +static int alsa_device_exit(struct saa7134_dev *dev) +{ + + snd_card_free(snd_saa7134_cards[dev->nr]); + snd_saa7134_cards[dev->nr] = NULL; + return 1; +} + +/* + * Module initializer + * + * Loops through present saa7134 cards, and assigns an ALSA device + * to each one + * + */ + +static int saa7134_alsa_init(void) +{ + struct saa7134_dev *dev = NULL; + struct list_head *list; + + saa7134_dmasound_init = alsa_device_init; + saa7134_dmasound_exit = alsa_device_exit; + + printk(KERN_INFO "saa7134 ALSA driver for DMA sound loaded\n"); + + list_for_each(list,&saa7134_devlist) { + dev = list_entry(list, struct saa7134_dev, devlist); + if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130) + printk(KERN_INFO "%s/alsa: %s doesn't support digital audio\n", + dev->name, saa7134_boards[dev->board].name); + else + alsa_device_init(dev); + } + + if (dev == NULL) + printk(KERN_INFO "saa7134 ALSA: no saa7134 cards found\n"); + + return 0; + +} + +/* + * Module destructor + */ + +static void saa7134_alsa_exit(void) +{ + int idx; + + for (idx = 0; idx < SNDRV_CARDS; idx++) { + snd_card_free(snd_saa7134_cards[idx]); + } + + saa7134_dmasound_init = NULL; + saa7134_dmasound_exit = NULL; + printk(KERN_INFO "saa7134 ALSA driver for DMA sound unloaded\n"); + + return; +} + +/* We initialize this late, to make sure the sound system is up and running */ +late_initcall(saa7134_alsa_init); +module_exit(saa7134_alsa_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ricardo Cerqueira"); diff --git a/drivers/media/pci/saa7134/saa7134-cards.c b/drivers/media/pci/saa7134/saa7134-cards.c new file mode 100644 index 000000000000..bc08f1dbc293 --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-cards.c @@ -0,0 +1,8026 @@ +/* + * + * device driver for philips saa7134 based TV cards + * card-specific stuff. + * + * (c) 2001-04 Gerd Knorr <kraxel@bytesex.org> [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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + +#include "saa7134-reg.h" +#include "saa7134.h" +#include "tuner-xc2028.h" +#include <media/v4l2-common.h> +#include <media/tveeprom.h> +#include "tea5767.h" +#include "tda18271.h" +#include "xc5000.h" +#include "s5h1411.h" + +/* commly used strings */ +static char name_mute[] = "mute"; +static char name_radio[] = "Radio"; +static char name_tv[] = "Television"; +static char name_tv_mono[] = "TV (mono only)"; +static char name_comp[] = "Composite"; +static char name_comp1[] = "Composite1"; +static char name_comp2[] = "Composite2"; +static char name_comp3[] = "Composite3"; +static char name_comp4[] = "Composite4"; +static char name_svideo[] = "S-Video"; + +/* ------------------------------------------------------------------ */ +/* board config info */ + +/* If radio_type !=UNSET, radio_addr should be specified + */ + +struct saa7134_board saa7134_boards[] = { + [SAA7134_BOARD_UNKNOWN] = { + .name = "UNKNOWN/GENERIC", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + + .inputs = {{ + .name = "default", + .vmux = 0, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_PROTEUS_PRO] = { + /* /me */ + .name = "Proteus Pro [philips reference design]", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + + .inputs = {{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_FLYVIDEO3000] = { + /* "Marco d'Itri" <md@Linux.IT> */ + .name = "LifeView FlyVIDEO3000", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + + .gpiomask = 0xe000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x8000, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + .gpio = 0x4000, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + .gpio = 0x4000, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x4000, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x2000, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x8000, + }, + }, + [SAA7134_BOARD_FLYVIDEO2000] = { + /* "TC Wan" <tcwan@cs.usm.my> */ + .name = "LifeView/Typhoon FlyVIDEO2000", + .audio_clock = 0x00200000, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + + .gpiomask = 0xe000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + .gpio = 0x4000, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + .gpio = 0x4000, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x4000, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x2000, + }, + .mute = { + .name = name_mute, + .amux = LINE2, + .gpio = 0x8000, + }, + }, + [SAA7134_BOARD_FLYTVPLATINUM_MINI] = { + /* "Arnaud Quette" <aquette@free.fr> */ + .name = "LifeView FlyTV Platinum Mini", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, /* Composite signal on S-Video input */ + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, /* Composite input */ + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_FLYTVPLATINUM_FM] = { + /* LifeView FlyTV Platinum FM (LR214WF) */ + /* "Peter Missel <peter.missel@onlinehome.de> */ + .name = "LifeView FlyTV Platinum FM / Gold", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + + .gpiomask = 0x1E000, /* Set GP16 and unused 15,14,13 to Output */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x10000, /* GP16=1 selects TV input */ + .tv = 1, + },{ +/* .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + },{ +*/ .name = name_comp1, /* Composite signal on S-Video input */ + .vmux = 0, + .amux = LINE2, +/* .gpio = 0x4000, */ + },{ + .name = name_comp2, /* Composite input */ + .vmux = 3, + .amux = LINE2, +/* .gpio = 0x4000, */ + },{ + .name = name_svideo, /* S-Video signal on S-Video input */ + .vmux = 8, + .amux = LINE2, +/* .gpio = 0x4000, */ + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x00000, /* GP16=0 selects FM radio antenna */ + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x10000, + }, + }, + [SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM] = { + /* RoverMedia TV Link Pro FM (LR138 REV:I) */ + /* Eugene Yudin <Eugene.Yudin@gmail.com> */ + .name = "RoverMedia TV Link Pro FM", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* TCL MFPE05 2 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0xe000, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x8000, + .tv = 1, + }, { + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + .gpio = 0x4000, + }, { + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + .gpio = 0x4000, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x4000, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x2000, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x8000, + }, + }, + [SAA7134_BOARD_EMPRESS] = { + /* "Gert Vervoort" <gert.vervoort@philips.com> */ + .name = "EMPRESS", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, + + .inputs = {{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + .mpeg = SAA7134_MPEG_EMPRESS, + .video_out = CCIR656, + }, + [SAA7134_BOARD_MONSTERTV] = { + /* "K.Ohta" <alpha292@bremen.or.jp> */ + .name = "SKNet Monster TV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_MD9717] = { + .name = "Tevion MD 9717", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + /* workaround for problems with normal TV sound */ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + .mute = { + .name = name_mute, + .amux = TV, + }, + }, + [SAA7134_BOARD_TVSTATION_RDS] = { + /* Typhoon TV Tuner RDS: Art.Nr. 50694 */ + .name = "KNC One TV-Station RDS / Typhoon TV Tuner RDS", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + + .name = "CVid over SVid", + .vmux = 0, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_TVSTATION_DVR] = { + .name = "KNC One TV-Station DVR", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x820000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x20000, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x20000, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x20000, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x20000, + }, + .mpeg = SAA7134_MPEG_EMPRESS, + .video_out = CCIR656, + }, + [SAA7134_BOARD_CINERGY400] = { + .name = "Terratec Cinergy 400 TV", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 4, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp2, /* CVideo over SVideo Connector */ + .vmux = 0, + .amux = LINE1, + }} + }, + [SAA7134_BOARD_MD5044] = { + .name = "Medion 5044", + .audio_clock = 0x00187de7, /* was: 0x00200000, */ + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + /* workaround for problems with normal TV sound */ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_KWORLD] = { + .name = "Kworld/KuroutoShikou SAA7130-TVPCI", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }}, + }, + [SAA7134_BOARD_CINERGY600] = { + .name = "Terratec Cinergy 600 TV", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 4, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp2, /* CVideo over SVideo Connector */ + .vmux = 0, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_MD7134] = { + .name = "Medion 7134", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + .mute = { + .name = name_mute, + .amux = TV, + }, + }, + [SAA7134_BOARD_TYPHOON_90031] = { + /* aka Typhoon "TV+Radio", Art.Nr 90031 */ + /* Tom Zoerner <tomzo at users sourceforge net> */ + .name = "Typhoon TV+Radio 90031", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_ELSA] = { + .name = "ELSA EX-VISION 300TV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_HITACHI_NTSC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 4, + .amux = LINE2, + .tv = 1, + }}, + }, + [SAA7134_BOARD_ELSA_500TV] = { + .name = "ELSA EX-VISION 500TV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_HITACHI_NTSC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_svideo, + .vmux = 7, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 8, + .amux = TV, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 8, + .amux = LINE2, + .tv = 1, + }}, + }, + [SAA7134_BOARD_ELSA_700TV] = { + .name = "ELSA EX-VISION 700TV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_HITACHI_NTSC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 4, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 6, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 7, + .amux = LINE1, + }}, + .mute = { + .name = name_mute, + .amux = TV, + }, + }, + [SAA7134_BOARD_ASUSTeK_TVFM7134] = { + .name = "ASUS TV-FM 7134", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 4, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_ASUSTeK_TVFM7135] = { + .name = "ASUS TV-FM 7135", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x0000, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 4, + .amux = LINE2, + .gpio = 0x0000, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE2, + .gpio = 0x0000, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x200000, + }, + .mute = { + .name = name_mute, + .gpio = 0x0000, + }, + + }, + [SAA7134_BOARD_VA1000POWER] = { + .name = "AOPEN VA1000 POWER", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_NTSC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }}, + }, + [SAA7134_BOARD_10MOONSTVMASTER] = { + /* "lilicheng" <llc@linuxfans.org> */ + .name = "10MOONS PCI TV CAPTURE CARD", + .audio_clock = 0x00200000, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0xe000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + .gpio = 0x4000, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + .gpio = 0x4000, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x4000, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x2000, + }, + .mute = { + .name = name_mute, + .amux = LINE2, + .gpio = 0x8000, + }, + }, + [SAA7134_BOARD_BMK_MPEX_NOTUNER] = { + /* "Andrew de Quincey" <adq@lidskialf.net> */ + .name = "BMK MPEX No Tuner", + .audio_clock = 0x200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, + .inputs = {{ + .name = name_comp1, + .vmux = 4, + .amux = LINE1, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_comp3, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_comp4, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .mpeg = SAA7134_MPEG_EMPRESS, + .video_out = CCIR656, + }, + [SAA7134_BOARD_VIDEOMATE_TV] = { + .name = "Compro VideoMate TV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }}, + }, + [SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS] = { + .name = "Compro VideoMate TV Gold+", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .gpiomask = 0x800c0000, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x06c00012, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x0ac20012, + },{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .gpio = 0x08c20012, + .tv = 1, + }}, /* radio and probably mute is missing */ + }, + [SAA7134_BOARD_CRONOS_PLUS] = { + /* + gpio pins: + 0 .. 3 BASE_ID + 4 .. 7 PROTECT_ID + 8 .. 11 USER_OUT + 12 .. 13 USER_IN + 14 .. 15 VIDIN_SEL + */ + .name = "Matrox CronosPlus", + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0xcf00, + .inputs = {{ + .name = name_comp1, + .vmux = 0, + .gpio = 2 << 14, + },{ + .name = name_comp2, + .vmux = 0, + .gpio = 1 << 14, + },{ + .name = name_comp3, + .vmux = 0, + .gpio = 0 << 14, + },{ + .name = name_comp4, + .vmux = 0, + .gpio = 3 << 14, + },{ + .name = name_svideo, + .vmux = 8, + .gpio = 2 << 14, + }}, + }, + [SAA7134_BOARD_MD2819] = { + .name = "AverMedia M156 / Medion 2819", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x03, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x00, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x02, + }, { + .name = name_comp2, + .vmux = 0, + .amux = LINE1, + .gpio = 0x02, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x02, + } }, + .radio = { + .name = name_radio, + .amux = LINE1, + .gpio = 0x01, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x00, + }, + }, + [SAA7134_BOARD_BMK_MPEX_TUNER] = { + /* "Greg Wickham <greg.wickham@grangenet.net> */ + .name = "BMK MPEX Tuner", + .audio_clock = 0x200000, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, + .inputs = {{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }}, + .mpeg = SAA7134_MPEG_EMPRESS, + .video_out = CCIR656, + }, + [SAA7134_BOARD_ASUSTEK_TVFM7133] = { + .name = "ASUS TV-FM 7133", + .audio_clock = 0x00187de7, + /* probably wrong, the 7133 one is the NTSC version ... + * .tuner_type = TUNER_PHILIPS_FM1236_MK3 */ + .tuner_type = TUNER_LG_NTSC_NEW_TAPC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + + },{ + .name = name_comp1, + .vmux = 4, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_PINNACLE_PCTV_STEREO] = { + .name = "Pinnacle PCTV Stereo (saa7134)", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_MT2032, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER | TDA9887_PORT2_INACTIVE, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, + .vmux = 1, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_MANLI_MTV002] = { + /* Ognjen Nastic <ognjen@logosoft.ba> */ + .name = "Manli MuchTV M-TV002", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_MANLI_MTV001] = { + /* Ognjen Nastic <ognjen@logosoft.ba> UNTESTED */ + .name = "Manli MuchTV M-TV001", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }}, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_TG3000TV] = { + /* TransGear 3000TV */ + .name = "Nagase Sangyo TransGear 3000TV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_ECS_TVP3XP] = { + .name = "Elitegroup ECS TVP3XP FM1216 Tuner Card(PAL-BG,FM) ", + .audio_clock = 0x187de7, /* xtal 32.1 MHz */ + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = "CVid over SVid", + .vmux = 0, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_ECS_TVP3XP_4CB5] = { + .name = "Elitegroup ECS TVP3XP FM1236 Tuner Card (NTSC,FM)", + .audio_clock = 0x187de7, + .tuner_type = TUNER_PHILIPS_NTSC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = "CVid over SVid", + .vmux = 0, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_ECS_TVP3XP_4CB6] = { + /* Barry Scott <barry.scott@onelan.co.uk> */ + .name = "Elitegroup ECS TVP3XP FM1246 Tuner Card (PAL,FM)", + .audio_clock = 0x187de7, + .tuner_type = TUNER_PHILIPS_PAL_I, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = "CVid over SVid", + .vmux = 0, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_AVACSSMARTTV] = { + /* Roman Pszonczenko <romka@kolos.math.uni.lodz.pl> */ + .name = "AVACS SmartTV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x200000, + }, + }, + [SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER] = { + /* Michael Smith <msmith@cbnco.com> */ + .name = "AVerMedia DVD EZMaker", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_comp1, + .vmux = 3, + },{ + .name = name_svideo, + .vmux = 8, + }}, + }, + [SAA7134_BOARD_AVERMEDIA_M103] = { + /* Massimo Piccioni <dafastidio@libero.it> */ + .name = "AVerMedia MiniPCI DVB-T Hybrid M103", + .audio_clock = 0x187de7, + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + } }, + }, + [SAA7134_BOARD_NOVAC_PRIMETV7133] = { + /* toshii@netbsd.org */ + .name = "Noval Prime TV 7133", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ALPS_TSBH1_NTSC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_comp1, + .vmux = 3, + },{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_svideo, + .vmux = 8, + }}, + }, + [SAA7134_BOARD_AVERMEDIA_STUDIO_305] = { + .name = "AverMedia AverTV Studio 305", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1256_IH3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_AVERMEDIA_STUDIO_505] = { + /* Vasiliy Temnikov <vaka@newmail.ru> */ + .name = "AverMedia AverTV Studio 505", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_UPMOST_PURPLE_TV] = { + .name = "UPMOST PURPLE TV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1236_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 7, + .amux = TV, + .tv = 1, + },{ + .name = name_svideo, + .vmux = 7, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_ITEMS_MTV005] = { + /* Norman Jonas <normanjonas@arcor.de> */ + .name = "Items MuchTV Plus / IT-005", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_CINERGY200] = { + .name = "Terratec Cinergy 200 TV", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 4, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp2, /* CVideo over SVideo Connector */ + .vmux = 0, + .amux = LINE1, + }}, + .mute = { + .name = name_mute, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_VIDEOMATE_TV_PVR] = { + /* Alain St-Denis <alain@topaze.homeip.net> */ + .name = "Compro VideoMate TV PVR/FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x808c0080, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x00080, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x00080, + },{ + .name = name_tv, + .vmux = 1, + .amux = LINE2_LEFT, + .tv = 1, + .gpio = 0x00080, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x80000, + }, + .mute = { + .name = name_mute, + .amux = LINE2, + .gpio = 0x40000, + }, + }, + [SAA7134_BOARD_SABRENT_SBTTVFM] = { + /* Michael Rodriguez-Torrent <mrtorrent@asu.edu> */ + .name = "Sabrent SBT-TVFM (saa7130)", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_NTSC_M, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_ZOLID_XPERT_TV7134] = { + /* Helge Jensen <helge.jensen@slog.dk> */ + .name = ":Zolid Xpert TV7134", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_NTSC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }}, + }, + [SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE] = { + /* "Matteo Az" <matte.az@nospam.libero.it> ;-) */ + .name = "Empire PCI TV-Radio LE", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x4000, + .inputs = {{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .gpio = 0x8000, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x8000, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + .gpio = 0x8000, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + .gpio = 0x8000, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio =0x8000, + } + }, + [SAA7134_BOARD_AVERMEDIA_STUDIO_307] = { + /* + Nickolay V. Shmyrev <nshmyrev@yandex.ru> + Lots of thanks to Andrey Zolotarev <zolotarev_andrey@mail.ru> + */ + .name = "Avermedia AVerTV Studio 307", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1256_IH3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x03, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x00, + },{ + .name = name_comp, + .vmux = 3, + .amux = LINE1, + .gpio = 0x02, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x02, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + .gpio = 0x01, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + .gpio = 0x00, + }, + }, + [SAA7134_BOARD_AVERMEDIA_GO_007_FM] = { + .name = "Avermedia AVerTV GO 007 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x00300003, + /* .gpiomask = 0x8c240003, */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x01, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + .gpio = 0x02, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + .gpio = 0x02, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x00300001, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x01, + }, + }, + [SAA7134_BOARD_AVERMEDIA_CARDBUS] = { + /* Kees.Blom@cwi.nl */ + .name = "AVerMedia Cardbus TV/Radio (E500)", + .audio_clock = 0x187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_AVERMEDIA_CARDBUS_501] = { + /* Oldrich Jedlicka <oldium.pro@seznam.cz> */ + .name = "AVerMedia Cardbus TV/Radio (E501R)", + .audio_clock = 0x187de7, + .tuner_type = TUNER_ALPS_TSBE5_PAL, + .radio_type = TUNER_TEA5767, + .tuner_addr = 0x61, + .radio_addr = 0x60, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x08000000, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x08000000, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x08000000, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x08000000, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x00000000, + }, + }, + [SAA7134_BOARD_CINERGY400_CARDBUS] = { + .name = "Terratec Cinergy 400 mobile", + .audio_clock = 0x187de7, + .tuner_type = TUNER_ALPS_TSBE5_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_CINERGY600_MK3] = { + .name = "Terratec Cinergy 600 TV MK3", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 4, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp2, /* CVideo over SVideo Connector */ + .vmux = 0, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_VIDEOMATE_GOLD_PLUS] = { + /* Dylan Walkden <dylan_walkden@hotmail.com> */ + .name = "Compro VideoMate Gold+ Pal", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_PAL, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x1ce780, + .inputs = {{ + .name = name_svideo, + .vmux = 0, /* CVideo over SVideo Connector - ok? */ + .amux = LINE1, + .gpio = 0x008080, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x008080, + },{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x008080, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x80000, + }, + .mute = { + .name = name_mute, + .amux = LINE2, + .gpio = 0x0c8000, + }, + }, + [SAA7134_BOARD_PINNACLE_300I_DVBT_PAL] = { + .name = "Pinnacle PCTV 300i DVB-T + PAL", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_MT2032, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER | TDA9887_PORT2_INACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, + .vmux = 1, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_PROVIDEO_PV952] = { + /* andreas.kretschmer@web.de */ + .name = "ProVideo PV952", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_AVERMEDIA_305] = { + /* much like the "studio" version but without radio + * and another tuner (sirspiritus@yandex.ru) */ + .name = "AverMedia AverTV/305", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FQ1216ME, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_FLYDVBTDUO] = { + /* LifeView FlyDVB-T DUO */ + /* "Nico Sabbi <nsabbi@tiscali.it> Hartmut Hackmann hartmut.hackmann@t-online.de*/ + .name = "LifeView FlyDVB-T DUO / MSI TV@nywhere Duo", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x00200000, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x200000, /* GPIO21=High for TV input */ + .tv = 1, + },{ + .name = name_comp1, /* Composite signal on S-Video input */ + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, /* Composite input */ + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, /* S-Video signal on S-Video input */ + .vmux = 8, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ + }, + }, + [SAA7134_BOARD_PHILIPS_TOUGH] = { + .name = "Philips TOUGH DVB-T reference design", + .tuner_type = TUNER_ABSENT, + .audio_clock = 0x00187de7, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_AVERMEDIA_307] = { + /* + Davydov Vladimir <vladimir@iqmedia.com> + */ + .name = "Avermedia AVerTV 307", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FQ1216ME, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_ADS_INSTANT_TV] = { + .name = "ADS Tech Instant TV (saa7135)", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_KWORLD_VSTREAM_XPERT] = { + .name = "Kworld/Tevion V-Stream Xpert TV PVR7134", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_PAL_I, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x0700, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x000, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x200, /* gpio by DScaler */ + },{ + .name = name_svideo, + .vmux = 0, + .amux = LINE1, + .gpio = 0x200, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + .gpio = 0x100, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x000, + }, + }, + [SAA7134_BOARD_FLYDVBT_DUO_CARDBUS] = { + .name = "LifeView/Typhoon/Genius FlyDVB-T Duo Cardbus", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x00200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x200000, /* GPIO21=High for TV input */ + .tv = 1, + },{ + .name = name_svideo, /* S-Video signal on S-Video input */ + .vmux = 8, + .amux = LINE2, + },{ + .name = name_comp1, /* Composite signal on S-Video input */ + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, /* Composite input */ + .vmux = 3, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ + }, + }, + [SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII] = { + .name = "Compro VideoMate TV Gold+II", + .audio_clock = 0x002187de7, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .radio_type = TUNER_TEA5767, + .tuner_addr = 0x63, + .radio_addr = 0x60, + .gpiomask = 0x8c1880, + .inputs = {{ + .name = name_svideo, + .vmux = 0, + .amux = LINE1, + .gpio = 0x800800, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x801000, + },{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x800000, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x880000, + }, + .mute = { + .name = name_mute, + .amux = LINE2, + .gpio = 0x840000, + }, + }, + [SAA7134_BOARD_KWORLD_XPERT] = { + /* + FIXME: + - Remote control doesn't initialize properly. + - Audio volume starts muted, + then gradually increases after channel change. + - Overlay scaling problems (application error?) + - Composite S-Video untested. + From: Konrad Rzepecki <hannibal@megapolis.pl> + */ + .name = "Kworld Xpert TV PVR7134", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_TENA_9533_DI, + .radio_type = TUNER_TEA5767, + .tuner_addr = 0x61, + .radio_addr = 0x60, + .gpiomask = 0x0700, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x000, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x200, /* gpio by DScaler */ + },{ + .name = name_svideo, + .vmux = 0, + .amux = LINE1, + .gpio = 0x200, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + .gpio = 0x100, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x000, + }, + }, + [SAA7134_BOARD_FLYTV_DIGIMATRIX] = { + .name = "FlyTV mini Asus Digimatrix", + .audio_clock = 0x00200000, + .tuner_type = TUNER_LG_TALN, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, /* radio unconfirmed */ + .amux = LINE2, + }, + }, + [SAA7134_BOARD_KWORLD_TERMINATOR] = { + /* Kworld V-Stream Studio TV Terminator */ + /* "James Webb <jrwebb@qwest.net> */ + .name = "V-Stream Studio TV Terminator", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 1 << 21, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x0000000, + .tv = 1, + },{ + .name = name_comp1, /* Composite input */ + .vmux = 3, + .amux = LINE2, + .gpio = 0x0000000, + },{ + .name = name_svideo, /* S-Video input */ + .vmux = 8, + .amux = LINE2, + .gpio = 0x0000000, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_YUAN_TUN900] = { + /* FIXME: + * S-Video and composite sources untested. + * Radio not working. + * Remote control not yet implemented. + * From : codemaster@webgeeks.be */ + .name = "Yuan TUN-900 (saa7135)", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr= ADDR_UNSET, + .radio_addr= ADDR_UNSET, + .gpiomask = 0x00010003, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x01, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + .gpio = 0x02, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE2, + .gpio = 0x02, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + .gpio = 0x00010003, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x01, + }, + }, + [SAA7134_BOARD_BEHOLD_409FM] = { + /* <http://tuner.beholder.ru>, Sergey <skiv@orel.ru> */ + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 409 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_GOTVIEW_7135] = { + /* Mike Baikov <mike@baikov.com> */ + /* Andrey Cvetcov <ays14@yandex.ru> */ + .name = "GoTView 7135 PCI", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00200003, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x00200003, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .gpio = 0x00200003, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x00200003, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x00200003, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x00200003, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x00200003, + }, + }, + [SAA7134_BOARD_PHILIPS_EUROPA] = { + .name = "Philips EUROPA V3 reference design", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TD1316, + .radio_type = UNSET, + .tuner_addr = 0x61, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_VIDEOMATE_DVBT_300] = { + .name = "Compro Videomate DVB-T300", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TD1316, + .radio_type = UNSET, + .tuner_addr = 0x61, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_VIDEOMATE_DVBT_200] = { + .name = "Compro Videomate DVB-T200", + .tuner_type = TUNER_ABSENT, + .audio_clock = 0x00187de7, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_RTD_VFG7350] = { + .name = "RTD Embedded Technologies VFG7350", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .empress_addr = 0x21, + .inputs = {{ + .name = "Composite 0", + .vmux = 0, + .amux = LINE1, + },{ + .name = "Composite 1", + .vmux = 1, + .amux = LINE2, + },{ + .name = "Composite 2", + .vmux = 2, + .amux = LINE1, + },{ + .name = "Composite 3", + .vmux = 3, + .amux = LINE2, + },{ + .name = "S-Video 0", + .vmux = 8, + .amux = LINE1, + },{ + .name = "S-Video 1", + .vmux = 9, + .amux = LINE2, + }}, + .mpeg = SAA7134_MPEG_EMPRESS, + .video_out = CCIR656, + .vid_port_opts = ( SET_T_CODE_POLARITY_NON_INVERTED | + SET_CLOCK_NOT_DELAYED | + SET_CLOCK_INVERTED | + SET_VSYNC_OFF ), + }, + [SAA7134_BOARD_RTD_VFG7330] = { + .name = "RTD Embedded Technologies VFG7330", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = "Composite 0", + .vmux = 0, + .amux = LINE1, + },{ + .name = "Composite 1", + .vmux = 1, + .amux = LINE2, + },{ + .name = "Composite 2", + .vmux = 2, + .amux = LINE1, + },{ + .name = "Composite 3", + .vmux = 3, + .amux = LINE2, + },{ + .name = "S-Video 0", + .vmux = 8, + .amux = LINE1, + },{ + .name = "S-Video 1", + .vmux = 9, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_FLYTVPLATINUM_MINI2] = { + .name = "LifeView FlyTV Platinum Mini2", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, /* Composite signal on S-Video input */ + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, /* Composite input */ + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180] = { + /* Michael Krufky <mkrufky@m1k.net> + * Uses Alps Electric TDHU2, containing NXT2004 ATSC Decoder + * AFAIK, there is no analog demod, thus, + * no support for analog television. + */ + .name = "AVerMedia AVerTVHD MCE A180", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_MONSTERTV_MOBILE] = { + .name = "SKNet MonsterTV Mobile", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_PINNACLE_PCTV_110i] = { + .name = "Pinnacle PCTV 40i/50i/110i (saa7133)", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x080200000, + .inputs = { { + .name = name_tv, + .vmux = 4, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE2, + }, { + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_ASUSTeK_P7131_DUAL] = { + .name = "ASUSTeK P7131 Dual", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 1 << 21, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000000, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + .gpio = 0x0200000, + },{ + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + .gpio = 0x0200000, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x0200000, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_SEDNA_PC_TV_CARDBUS] = { + /* Paul Tom Zalac <pzalac@gmail.com> */ + /* Pavel Mihaylov <bin@bash.info> */ + .name = "Sedna/MuchTV PC TV Cardbus TV/Radio (ITO25 Rev:2B)", + /* Sedna/MuchTV (OEM) Cardbus TV Tuner */ + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0xe880c0, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV] = { + /* "Cyril Lacoux (Yack)" <clacoux@ifeelgood.org> */ + .name = "ASUS Digimatrix TV", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FQ1216ME, + .tda9887_conf = TDA9887_PRESENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_PHILIPS_TIGER] = { + .name = "Philips Tiger reference design", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 0, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_MSI_TVATANYWHERE_PLUS] = { + .name = "MSI TV@Anywhere plus", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 1 << 21, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, /* unconfirmed, taken from Philips driver */ + },{ + .name = name_comp2, + .vmux = 0, /* untested, Composite over S-Video */ + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_CINERGY250PCI] = { + /* remote-control does not work. The signal about a + key press comes in via gpio, but the key code + doesn't. Neither does it have an i2c remote control + interface. */ + .name = "Terratec Cinergy 250 PCI TV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x80200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_svideo, /* NOT tested */ + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_FLYDVB_TRIO] = { + /* LifeView LR319 FlyDVB Trio */ + /* Peter Missel <peter.missel@onlinehome.de> */ + .name = "LifeView FlyDVB Trio", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x00200000, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, /* Analog broadcast/cable TV */ + .vmux = 1, + .amux = TV, + .gpio = 0x200000, /* GPIO21=High for TV input */ + .tv = 1, + },{ + .name = name_svideo, /* S-Video signal on S-Video input */ + .vmux = 8, + .amux = LINE2, + },{ + .name = name_comp1, /* Composite signal on S-Video input */ + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, /* Composite input */ + .vmux = 3, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ + }, + }, + [SAA7134_BOARD_AVERMEDIA_777] = { + .name = "AverTV DVB-T 777", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_FLYDVBT_LR301] = { + /* LifeView FlyDVB-T */ + /* Giampiero Giancipoli <gianci@libero.it> */ + .name = "LifeView FlyDVB-T / Genius VideoWonder DVB-T", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_comp1, /* Composite input */ + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, /* S-Video signal on S-Video input */ + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331] = { + .name = "ADS Instant TV Duo Cardbus PTV331", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x00200000, + }}, + }, + [SAA7134_BOARD_TEVION_DVBT_220RF] = { + .name = "Tevion/KWorld DVB-T 220RF", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 1 << 21, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_comp2, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_KWORLD_DVBT_210] = { + .name = "KWorld DVB-T 210", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 1 << 21, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_KWORLD_ATSC110] = { + .name = "Kworld ATSC110/115", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TUV1236D, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_AVERMEDIA_A169_B] = { + /* AVerMedia A169 */ + /* Rickard Osser <ricky@osser.se> */ + /* This card has two saa7134 chips on it, + but only one of them is currently working. */ + .name = "AVerMedia A169 B", + .audio_clock = 0x02187de7, + .tuner_type = TUNER_LG_TALN, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x0a60000, + }, + [SAA7134_BOARD_AVERMEDIA_A169_B1] = { + /* AVerMedia A169 */ + /* Rickard Osser <ricky@osser.se> */ + .name = "AVerMedia A169 B1", + .audio_clock = 0x02187de7, + .tuner_type = TUNER_LG_TALN, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0xca60000, + .inputs = {{ + .name = name_tv, + .vmux = 4, + .amux = TV, + .tv = 1, + .gpio = 0x04a61000, + },{ + .name = name_comp2, /* Composite SVIDEO (B/W if signal is carried with SVIDEO) */ + .vmux = 1, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 9, /* 9 is correct as S-VIDEO1 according to a169.inf! */ + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_MD7134_BRIDGE_2] = { + /* The second saa7134 on this card only serves as DVB-S host bridge */ + .name = "Medion 7134 Bridge #2", + .audio_clock = 0x00187de7, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + }, + [SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS] = { + .name = "LifeView FlyDVB-T Hybrid Cardbus/MSI TV @nywhere A/D NB", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x00600000, /* Bit 21 0=Radio, Bit 22 0=TV */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x200000, /* GPIO21=High for TV input */ + .tv = 1, + },{ + .name = name_svideo, /* S-Video signal on S-Video input */ + .vmux = 8, + .amux = LINE2, + },{ + .name = name_comp1, /* Composite signal on S-Video input */ + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, /* Composite input */ + .vmux = 3, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ + }, + }, + [SAA7134_BOARD_FLYVIDEO3000_NTSC] = { + /* "Zac Bowling" <zac@zacbowling.com> */ + .name = "LifeView FlyVIDEO3000 (NTSC)", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_NTSC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + + .gpiomask = 0xe000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .gpio = 0x8000, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + .gpio = 0x4000, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + .gpio = 0x4000, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x4000, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x2000, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x8000, + }, + }, + [SAA7134_BOARD_MEDION_MD8800_QUADRO] = { + .name = "Medion Md8800 Quadro", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_FLYDVBS_LR300] = { + /* LifeView FlyDVB-s */ + /* Igor M. Liplianin <liplianin@tut.by> */ + .name = "LifeView FlyDVB-S /Acorp TV134DS", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_comp1, /* Composite input */ + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, /* S-Video signal on S-Video input */ + .vmux = 8, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_PROTEUS_2309] = { + .name = "Proteus Pro 2309", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_AVERMEDIA_A16AR] = { + /* Petr Baudis <pasky@ucw.cz> */ + .name = "AVerMedia TV Hybrid A16AR", + .audio_clock = 0x187de7, + .tuner_type = TUNER_PHILIPS_TD1316, /* untested */ + .radio_type = TUNER_TEA5767, /* untested */ + .tuner_addr = ADDR_UNSET, + .radio_addr = 0x60, + .tda9887_conf = TDA9887_PRESENT, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_ASUS_EUROPA2_HYBRID] = { + .name = "Asus Europa2 OEM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT| TDA9887_PORT1_ACTIVE | TDA9887_PORT2_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 4, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_PINNACLE_PCTV_310i] = { + .name = "Pinnacle PCTV 310i", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 1, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x000200000, + .inputs = {{ + .name = name_tv, + .vmux = 4, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE2, + },{ + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_AVERMEDIA_STUDIO_507] = { + /* Mikhail Fedotov <mo_fedotov@mail.ru> */ + .name = "Avermedia AVerTV Studio 507", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1256_IH3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x03, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x00, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + .gpio = 0x00, + },{ + .name = name_comp2, + .vmux = 3, + .amux = LINE2, + .gpio = 0x00, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x00, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x01, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + .gpio = 0x00, + }, + }, + [SAA7134_BOARD_VIDEOMATE_DVBT_200A] = { + /* Francis Barber <fedora@barber-family.id.au> */ + .name = "Compro Videomate DVB-T200A", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_HAUPPAUGE_HVR1110] = { + /* Thomas Genty <tomlohave@gmail.com> */ + /* David Bentham <db260179@hotmail.com> */ + .name = "Hauppauge WinTV-HVR1110 DVB-T/Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 1, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200100, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200100, + }, + }, + [SAA7134_BOARD_HAUPPAUGE_HVR1150] = { + .name = "Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 3, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_SERIAL, + .ts_force_val = 1, + .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0800100, /* GPIO 23 HI for FM */ + }, + }, + [SAA7134_BOARD_HAUPPAUGE_HVR1120] = { + .name = "Hauppauge WinTV-HVR1120 DVB-T/Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 3, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_SERIAL, + .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0800100, /* GPIO 23 HI for FM */ + }, + }, + [SAA7134_BOARD_CINERGY_HT_PCMCIA] = { + .name = "Terratec Cinergy HT PCMCIA", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_ENCORE_ENLTV] = { + /* Steven Walter <stevenrwalter@gmail.com> + Juan Pablo Sormani <sorman@gmail.com> */ + .name = "Encore ENLTV", + .audio_clock = 0x00200000, + .tuner_type = TUNER_TNF_5335MF, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = 3, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 7, + .amux = 4, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = 2, + },{ + .name = name_svideo, + .vmux = 0, + .amux = 2, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, +/* .gpio = 0x00300001,*/ + .gpio = 0x20000, + + }, + .mute = { + .name = name_mute, + .amux = 0, + }, + }, + [SAA7134_BOARD_ENCORE_ENLTV_FM] = { + /* Juan Pablo Sormani <sorman@gmail.com> */ + .name = "Encore ENLTV-FM", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FCV1236D, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = 3, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 7, + .amux = 4, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = 2, + },{ + .name = name_svideo, + .vmux = 0, + .amux = 2, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x20000, + + }, + .mute = { + .name = name_mute, + .amux = 0, + }, + }, + [SAA7134_BOARD_ENCORE_ENLTV_FM53] = { + .name = "Encore ENLTV-FM v5.3", + .audio_clock = 0x00200000, + .tuner_type = TUNER_TNF_5335MF, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x7000, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = 1, + .tv = 1, + .gpio = 0x50000, + }, { + .name = name_comp1, + .vmux = 3, + .amux = 2, + .gpio = 0x2000, + }, { + .name = name_svideo, + .vmux = 8, + .amux = 2, + .gpio = 0x2000, + } }, + .radio = { + .name = name_radio, + .vmux = 1, + .amux = 1, + }, + .mute = { + .name = name_mute, + .gpio = 0xf000, + .amux = 0, + }, + }, + [SAA7134_BOARD_ENCORE_ENLTV_FM3] = { + .name = "Encore ENLTV-FM 3", + .audio_clock = 0x02187de7, + .tuner_type = TUNER_TENA_TNF_5337, + .radio_type = TUNER_TEA5767, + .tuner_addr = 0x61, + .radio_addr = 0x60, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .vmux = 1, + .amux = LINE1, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + .gpio = 0x43000, + }, + }, + [SAA7134_BOARD_CINERGY_HT_PCI] = { + .name = "Terratec Cinergy HT PCI", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_PHILIPS_TIGER_S] = { + .name = "Philips Tiger - S Reference design", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_AVERMEDIA_M102] = { + .name = "Avermedia M102", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 1<<21, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_ASUS_P7131_4871] = { + .name = "ASUS P7131 4871", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0200000, + }}, + }, + [SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA] = { + .name = "ASUSTeK P7131 Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .gpiomask = 1 << 21, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000000, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + .gpio = 0x0200000, + },{ + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + .gpio = 0x0200000, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x0200000, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_ASUSTeK_P7131_ANALOG] = { + .name = "ASUSTeK P7131 Analog", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 1 << 21, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000000, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + }, { + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_SABRENT_TV_PCB05] = { + .name = "Sabrent PCMCIA TV-PCB05", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_comp2, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .mute = { + .name = name_mute, + .amux = TV, + }, + }, + [SAA7134_BOARD_10MOONSTVMASTER3] = { + /* Tony Wan <aloha_cn@hotmail.com> */ + .name = "10MOONS TM300 TV Card", + .audio_clock = 0x00200000, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x7000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x2000, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x2000, + }}, + .mute = { + .name = name_mute, + .amux = LINE2, + .gpio = 0x3000, + }, + }, + [SAA7134_BOARD_AVERMEDIA_SUPER_007] = { + .name = "Avermedia Super 007", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 0, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, /* FIXME: analog tv untested */ + .vmux = 1, + .amux = TV, + .tv = 1, + }}, + }, + [SAA7134_BOARD_AVERMEDIA_M135A] = { + .name = "Avermedia PCI pure analog (M135A)", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .gpiomask = 0x020200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x00200000, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x01, + }, + }, + [SAA7134_BOARD_AVERMEDIA_M733A] = { + .name = "Avermedia PCI M733A", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 0, + .gpiomask = 0x020200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x00200000, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x01, + }, + }, + [SAA7134_BOARD_BEHOLD_401] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 401", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FQ1216ME, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }}, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_BEHOLD_403] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 403", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FQ1216ME, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }}, + }, + [SAA7134_BOARD_BEHOLD_403FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 403 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FQ1216ME, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_405] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 405", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }}, + }, + [SAA7134_BOARD_BEHOLD_405FM] = { + /* Sergey <skiv@orel.ru> */ + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 405 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_407] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 407", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0xc0c000, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + .gpio = 0xc0c000, + },{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + .gpio = 0xc0c000, + }}, + }, + [SAA7134_BOARD_BEHOLD_407FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 407 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0xc0c000, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + .gpio = 0xc0c000, + },{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + .gpio = 0xc0c000, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0xc0c000, + }, + }, + [SAA7134_BOARD_BEHOLD_409] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 409", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_BEHOLD_505FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 505 FM", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_505RDS_MK5] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 505 RDS", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FM1216MK5, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_507_9FM] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 507 FM / BeholdTV 509 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_507RDS_MK5] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 507 RDS", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216MK5, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_507RDS_MK3] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 507 RDS", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM] = { + /* Beholder Intl. Ltd. 2008 */ + /* Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV Columbus TV/FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ALPS_TSBE5_PAL, + .radio_type = TUNER_TEA5767, + .tuner_addr = 0xc2 >> 1, + .radio_addr = 0xc0 >> 1, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x000A8004, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + .gpio = 0x000A8004, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + .gpio = 0x000A8000, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x000A8000, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x000A8000, + }, + }, + [SAA7134_BOARD_BEHOLD_607FM_MK3] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 607 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_609FM_MK3] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 609 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_607FM_MK5] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 607 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216MK5, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_609FM_MK5] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 609 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216MK5, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_607RDS_MK3] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 607 RDS", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_609RDS_MK3] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 609 RDS", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_607RDS_MK5] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 607 RDS", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216MK5, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_609RDS_MK5] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 609 RDS", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216MK5, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_M6] = { + /* Igor Kuznetsov <igk@igk.ru> */ + /* Andrey Melnikoff <temnota@kmv.ru> */ + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + /* Alexey Osipov <lion-simba@pridelands.ru> */ + .name = "Beholder BeholdTV M6", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, + .tda9887_conf = TDA9887_PRESENT, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + .mpeg = SAA7134_MPEG_EMPRESS, + .video_out = CCIR656, + .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED | + SET_CLOCK_NOT_DELAYED | + SET_CLOCK_INVERTED | + SET_VSYNC_OFF), + }, + [SAA7134_BOARD_BEHOLD_M63] = { + /* Igor Kuznetsov <igk@igk.ru> */ + /* Andrey Melnikoff <temnota@kmv.ru> */ + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV M63", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, + .tda9887_conf = TDA9887_PRESENT, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + .mpeg = SAA7134_MPEG_EMPRESS, + .video_out = CCIR656, + .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED | + SET_CLOCK_NOT_DELAYED | + SET_CLOCK_INVERTED | + SET_VSYNC_OFF), + }, + [SAA7134_BOARD_BEHOLD_M6_EXTRA] = { + /* Igor Kuznetsov <igk@igk.ru> */ + /* Andrey Melnikoff <temnota@kmv.ru> */ + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + /* Alexey Osipov <lion-simba@pridelands.ru> */ + .name = "Beholder BeholdTV M6 Extra", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216MK5, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, + .empress_addr = 0x20, + .tda9887_conf = TDA9887_PRESENT, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + .mpeg = SAA7134_MPEG_EMPRESS, + .video_out = CCIR656, + .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED | + SET_CLOCK_NOT_DELAYED | + SET_CLOCK_INVERTED | + SET_VSYNC_OFF), + }, + [SAA7134_BOARD_TWINHAN_DTV_DVB_3056] = { + .name = "Twinhan Hybrid DTV-DVB 3056 PCI", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, /* untested */ + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_GENIUS_TVGO_A11MCE] = { + /* Adrian Pardini <pardo.bsso@gmail.com> */ + .name = "Genius TVGO AM11MCE", + .audio_clock = 0x00200000, + .tuner_type = TUNER_TNF_5335MF, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0xf000, + .inputs = {{ + .name = name_tv_mono, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x2000, + .tv = 1 + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x2000, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x1000, + }, + .mute = { + .name = name_mute, + .amux = LINE2, + .gpio = 0x6000, + }, + }, + [SAA7134_BOARD_PHILIPS_SNAKE] = { + .name = "NXP Snake DVB-S reference design", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + }, + [SAA7134_BOARD_CREATIX_CTX953] = { + .name = "Medion/Creatix CTX953 Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 0, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + }, + [SAA7134_BOARD_MSI_TVANYWHERE_AD11] = { + .name = "MSI TV@nywhere A/D v1.1", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_AVERMEDIA_CARDBUS_506] = { + .name = "AVerMedia Cardbus TV/Radio (E506R)", + .audio_clock = 0x187de7, + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, + [SAA7134_BOARD_AVERMEDIA_A16D] = { + .name = "AVerMedia Hybrid TV/Radio (A16D)", + .audio_clock = 0x187de7, + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }, { + .name = name_comp, + .vmux = 0, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, + [SAA7134_BOARD_AVERMEDIA_M115] = { + .name = "Avermedia M115", + .audio_clock = 0x187de7, + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + }, + [SAA7134_BOARD_VIDEOMATE_T750] = { + /* John Newbigin <jn@it.swin.edu.au> */ + .name = "Compro VideoMate T750", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = 0x61, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + } + }, + [SAA7134_BOARD_AVERMEDIA_A700_PRO] = { + /* Matthias Schwarzott <zzam@gentoo.org> */ + .name = "Avermedia DVB-S Pro A700", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_comp, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + } }, + }, + [SAA7134_BOARD_AVERMEDIA_A700_HYBRID] = { + /* Matthias Schwarzott <zzam@gentoo.org> */ + .name = "Avermedia DVB-S Hybrid+FM A700", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_XC2028, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_tv, + .vmux = 4, + .amux = TV, + .tv = 1, + }, { + .name = name_comp, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, + [SAA7134_BOARD_BEHOLD_H6] = { + /* Igor Kuznetsov <igk@igk.ru> */ + .name = "Beholder BeholdTV H6", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FMD1216MEX_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_ASUSTeK_TIGER_3IN1] = { + .name = "Asus Tiger 3in1", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .gpiomask = 1 << 21, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_ASUSTeK_PS3_100] = { + .name = "Asus My Cinema PS3-100", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .gpiomask = 1 << 21, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_REAL_ANGEL_220] = { + .name = "Zogis Real Angel 220", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_TNF_5335MF, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x801a8087, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + .gpio = 0x624000, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + .gpio = 0x624000, + }, { + .name = name_svideo, + .vmux = 1, + .amux = LINE1, + .gpio = 0x624000, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x624001, + }, + .mute = { + .name = name_mute, + .amux = TV, + }, + }, + [SAA7134_BOARD_ADS_INSTANT_HDTV_PCI] = { + .name = "ADS Tech Instant HDTV", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TUV1236D, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp, + .vmux = 4, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + }, + [SAA7134_BOARD_ASUSTeK_TIGER] = { + .name = "Asus Tiger Rev:1.00", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 0, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + }, { + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG] = { + .name = "Kworld Plus TV Analog Lite PCI", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_YMEC_TVF_5533MF, + .radio_type = TUNER_TEA5767, + .tuner_addr = ADDR_UNSET, + .radio_addr = 0x60, + .gpiomask = 0x80000700, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .tv = 1, + .gpio = 0x100, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x200, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x200, + } }, + .radio = { + .name = name_radio, + .vmux = 1, + .amux = LINE1, + .gpio = 0x100, + }, + .mute = { + .name = name_mute, + .vmux = 8, + .amux = 2, + }, + }, + [SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG] = { + .name = "Kworld PCI SBTVD/ISDB-T Full-Seg Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .tuner_addr = ADDR_UNSET, + .radio_type = UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x8e054000, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_PARALLEL, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, +#if 0 /* FIXME */ + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x200, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x200, +#endif + } }, +#if 0 + .radio = { + .name = name_radio, + .vmux = 1, + .amux = LINE1, + .gpio = 0x100, + }, +#endif + .mute = { + .name = name_mute, + .vmux = 0, + .amux = TV, + }, + }, + [SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS] = { + .name = "Avermedia AVerTV GO 007 FM Plus", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x00300003, + /* .gpiomask = 0x8c240003, */ + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x01, + }, { + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + .gpio = 0x02, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x00300001, + }, + .mute = { + .name = name_mute, + .amux = TV, + .gpio = 0x01, + }, + }, + [SAA7134_BOARD_AVERMEDIA_STUDIO_507UA] = { + /* Andy Shevchenko <andy@smile.org.ua> */ + .name = "Avermedia AVerTV Studio 507UA", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* Should be MK5 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x03, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x00, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x00, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x00, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x01, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + .gpio = 0x00, + }, + }, + [SAA7134_BOARD_VIDEOMATE_S350] = { + /* Jan D. Louw <jd.louw@mweb.co.za */ + .name = "Compro VideoMate S350/S300", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, /* Not tested */ + .amux = LINE1 + } }, + }, + [SAA7134_BOARD_BEHOLD_X7] = { + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV X7", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_XC5000, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_tv, + .vmux = 2, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 9, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, + [SAA7134_BOARD_ZOLID_HYBRID_PCI] = { + .name = "Zolid Hybrid TV Tuner PCI", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 0, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_PARALLEL, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + } }, + .radio = { /* untested */ + .name = name_radio, + .amux = TV, + }, + }, + [SAA7134_BOARD_ASUS_EUROPA_HYBRID] = { + .name = "Asus Europa Hybrid OEM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TD1316, + .radio_type = UNSET, + .tuner_addr = 0x61, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 4, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + }, + [SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S] = { + .name = "Leadtek Winfast DTV1000S", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_comp1, + .vmux = 3, + }, { + .name = name_svideo, + .vmux = 8, + } }, + }, + [SAA7134_BOARD_BEHOLD_505RDS_MK3] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 505 RDS", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_HAWELL_HW_404M7] = { + /* Hawell HW-404M7 & Hawell HW-808M7 */ + /* Bogoslovskiy Viktor <bogovic@bk.ru> */ + .name = "Hawell HW-404M7", + .audio_clock = 0x00200000, + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x389c00, + .inputs = {{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x01fc00, + } }, + }, + [SAA7134_BOARD_BEHOLD_H7] = { + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV H7", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_XC5000, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_PARALLEL, + .inputs = { { + .name = name_tv, + .vmux = 2, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 9, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, + [SAA7134_BOARD_BEHOLD_A7] = { + /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV A7", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_XC5000, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = { { + .name = name_tv, + .vmux = 2, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 9, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = TV, + }, + }, + [SAA7134_BOARD_TECHNOTREND_BUDGET_T3000] = { + .name = "TechoTrend TT-budget T-3000", + .tuner_type = TUNER_PHILIPS_TD1316, + .audio_clock = 0x00187de7, + .radio_type = UNSET, + .tuner_addr = 0x63, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + }, + [SAA7134_BOARD_VIDEOMATE_M1F] = { + /* Pavel Osnova <pvosnova@gmail.com> */ + .name = "Compro VideoMate Vista M1F", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .radio_type = TUNER_TEA5767, + .tuner_addr = ADDR_UNSET, + .radio_addr = 0x60, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = LINE1, + }, + .mute = { + .name = name_mute, + .amux = TV, + }, + }, + [SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2] = { + /* Timothy Lee <timothy.lee@siriushk.com> */ + .name = "MagicPro ProHDTV Pro2 DMB-TH/Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_config = 3, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x02050000, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_PARALLEL, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x00050000, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x00050000, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x00050000, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x00050000, + }, + .mute = { + .name = name_mute, + .vmux = 0, + .amux = TV, + .gpio = 0x00050000, + }, + }, + [SAA7134_BOARD_BEHOLD_501] = { + /* Beholder Intl. Ltd. 2010 */ + /* Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 501", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_BEHOLD_503FM] = { + /* Beholder Intl. Ltd. 2010 */ + /* Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 503 FM", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x00008000, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + }, + [SAA7134_BOARD_SENSORAY811_911] = { + .name = "Sensoray 811/911", + .audio_clock = 0x00200000, + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + }, { + .name = name_comp3, + .vmux = 2, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + }, + [SAA7134_BOARD_KWORLD_PC150U] = { + .name = "Kworld PC150-U", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 1 << 21, + .ts_type = SAA7134_MPEG_TS_PARALLEL, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp, + .vmux = 3, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0000000, + }, + }, + +}; + +const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); + +/* ------------------------------------------------------------------ */ +/* PCI ids + subsystem IDs */ + +struct pci_device_id saa7134_pci_tbl[] = { + { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2001, + .driver_data = SAA7134_BOARD_PROTEUS_PRO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2001, + .driver_data = SAA7134_BOARD_PROTEUS_PRO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x6752, + .driver_data = SAA7134_BOARD_EMPRESS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1131, + .subdevice = 0x4e85, + .driver_data = SAA7134_BOARD_MONSTERTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x153b, + .subdevice = 0x1142, + .driver_data = SAA7134_BOARD_CINERGY400, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x153b, + .subdevice = 0x1143, + .driver_data = SAA7134_BOARD_CINERGY600, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x153b, + .subdevice = 0x1158, + .driver_data = SAA7134_BOARD_CINERGY600_MK3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x153b, + .subdevice = 0x1162, + .driver_data = SAA7134_BOARD_CINERGY400_CARDBUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x5169, + .subdevice = 0x0138, + .driver_data = SAA7134_BOARD_FLYVIDEO3000_NTSC, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x5168, + .subdevice = 0x0138, + .driver_data = SAA7134_BOARD_FLYVIDEO3000, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x4e42, /* "Typhoon PCI Capture TV Card" Art.No. 50673 */ + .subdevice = 0x0138, + .driver_data = SAA7134_BOARD_FLYVIDEO3000, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x5168, + .subdevice = 0x0138, + .driver_data = SAA7134_BOARD_FLYVIDEO2000, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x4e42, /* Typhoon */ + .subdevice = 0x0138, /* LifeView FlyTV Prime30 OEM */ + .driver_data = SAA7134_BOARD_FLYVIDEO2000, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, + .subdevice = 0x0212, /* minipci, LR212 */ + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x14c0, + .subdevice = 0x1212, /* minipci, LR1212 */ + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI2, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x4e42, + .subdevice = 0x0212, /* OEM minipci, LR212 */ + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, /* Animation Technologies (LifeView) */ + .subdevice = 0x0214, /* Standard PCI, LR214 Rev E and earlier (SAA7135) */ + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, /* Animation Technologies (LifeView) */ + .subdevice = 0x5214, /* Standard PCI, LR214 Rev F onwards (SAA7131) */ + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1489, /* KYE */ + .subdevice = 0x0214, /* Genius VideoWonder ProTV */ + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, /* is an LR214WF actually */ + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x16be, + .subdevice = 0x0003, + .driver_data = SAA7134_BOARD_MD7134, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x16be, /* CTX946 analog TV, HW mpeg, DVB-T */ + .subdevice = 0x5000, /* only analog TV and DVB-T for now */ + .driver_data = SAA7134_BOARD_MD7134, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1048, + .subdevice = 0x226b, + .driver_data = SAA7134_BOARD_ELSA, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1048, + .subdevice = 0x226a, + .driver_data = SAA7134_BOARD_ELSA_500TV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1048, + .subdevice = 0x226c, + .driver_data = SAA7134_BOARD_ELSA_700TV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = PCI_VENDOR_ID_ASUSTEK, + .subdevice = 0x4842, + .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = PCI_VENDOR_ID_ASUSTEK, + .subdevice = 0x4845, + .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7135, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = PCI_VENDOR_ID_ASUSTEK, + .subdevice = 0x4830, + .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = PCI_VENDOR_ID_ASUSTEK, + .subdevice = 0x4843, + .driver_data = SAA7134_BOARD_ASUSTEK_TVFM7133, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = PCI_VENDOR_ID_ASUSTEK, + .subdevice = 0x4840, + .driver_data = SAA7134_BOARD_ASUSTeK_TVFM7134, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0xfe01, + .driver_data = SAA7134_BOARD_TVSTATION_RDS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1894, + .subdevice = 0xfe01, + .driver_data = SAA7134_BOARD_TVSTATION_RDS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1894, + .subdevice = 0xa006, + .driver_data = SAA7134_BOARD_TVSTATION_DVR, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1131, + .subdevice = 0x7133, + .driver_data = SAA7134_BOARD_VA1000POWER, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2001, + .driver_data = SAA7134_BOARD_10MOONSTVMASTER, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x185b, + .subdevice = 0xc100, + .driver_data = SAA7134_BOARD_VIDEOMATE_TV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x185b, + .subdevice = 0xc100, + .driver_data = SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = PCI_VENDOR_ID_MATROX, + .subdevice = 0x48d0, + .driver_data = SAA7134_BOARD_CRONOS_PLUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa70b, + .driver_data = SAA7134_BOARD_MD2819, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa7a1, + .driver_data = SAA7134_BOARD_AVERMEDIA_A700_PRO, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa7a2, + .driver_data = SAA7134_BOARD_AVERMEDIA_A700_HYBRID, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0x2115, + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_305, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa115, + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_505, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0x2108, + .driver_data = SAA7134_BOARD_AVERMEDIA_305, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0x10ff, + .driver_data = SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER, + },{ + /* AVerMedia CardBus */ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xd6ee, + .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS, + },{ + /* AVerMedia CardBus */ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xb7e9, + .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS_501, + }, { + /* TransGear 3000TV */ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0x050c, + .driver_data = SAA7134_BOARD_TG3000TV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x11bd, + .subdevice = 0x002b, + .driver_data = SAA7134_BOARD_PINNACLE_PCTV_STEREO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x11bd, + .subdevice = 0x002d, + .driver_data = SAA7134_BOARD_PINNACLE_300I_DVBT_PAL, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1019, + .subdevice = 0x4cb4, + .driver_data = SAA7134_BOARD_ECS_TVP3XP, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1019, + .subdevice = 0x4cb5, + .driver_data = SAA7134_BOARD_ECS_TVP3XP_4CB5, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1019, + .subdevice = 0x4cb6, + .driver_data = SAA7134_BOARD_ECS_TVP3XP_4CB6, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x12ab, + .subdevice = 0x0800, + .driver_data = SAA7134_BOARD_UPMOST_PURPLE_TV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x153b, + .subdevice = 0x1152, + .driver_data = SAA7134_BOARD_CINERGY200, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x185b, + .subdevice = 0xc100, + .driver_data = SAA7134_BOARD_VIDEOMATE_TV_PVR, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0x9715, + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_307, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa70a, + .driver_data = SAA7134_BOARD_AVERMEDIA_307, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x185b, + .subdevice = 0xc200, + .driver_data = SAA7134_BOARD_VIDEOMATE_GOLD_PLUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1540, + .subdevice = 0x9524, + .driver_data = SAA7134_BOARD_PROVIDEO_PV952, + + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, + .subdevice = 0x0502, /* Cardbus version */ + .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, + .subdevice = 0x0306, /* PCI version */ + .driver_data = SAA7134_BOARD_FLYDVBTDUO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf31f, + .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM, + + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf11d, + .driver_data = SAA7134_BOARD_AVERMEDIA_M135A, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0x4155, + .driver_data = SAA7134_BOARD_AVERMEDIA_M733A, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0x4255, + .driver_data = SAA7134_BOARD_AVERMEDIA_M733A, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2004, + .driver_data = SAA7134_BOARD_PHILIPS_TOUGH, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1421, + .subdevice = 0x0350, /* PCI version */ + .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1421, + .subdevice = 0x0351, /* PCI version, new revision */ + .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1421, + .subdevice = 0x0370, /* cardbus version */ + .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1421, + .subdevice = 0x1370, /* cardbus version */ + .driver_data = SAA7134_BOARD_ADS_INSTANT_TV, + + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x4e42, /* Typhoon */ + .subdevice = 0x0502, /* LifeView LR502 OEM */ + .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x0210, /* mini pci NTSC version */ + .driver_data = SAA7134_BOARD_FLYTV_DIGIMATRIX, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1043, + .subdevice = 0x0210, /* mini pci PAL/SECAM version */ + .driver_data = SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV, + + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0000, /* It shouldn't break anything, since subdevice id seems unique */ + .subdevice = 0x4091, + .driver_data = SAA7134_BOARD_BEHOLD_409FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5456, /* GoTView */ + .subdevice = 0x7135, + .driver_data = SAA7134_BOARD_GOTVIEW_7135, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2004, + .driver_data = SAA7134_BOARD_PHILIPS_EUROPA, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x185b, + .subdevice = 0xc900, + .driver_data = SAA7134_BOARD_VIDEOMATE_DVBT_300, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x185b, + .subdevice = 0xc901, + .driver_data = SAA7134_BOARD_VIDEOMATE_DVBT_200, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1435, + .subdevice = 0x7350, + .driver_data = SAA7134_BOARD_RTD_VFG7350, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1435, + .subdevice = 0x7330, + .driver_data = SAA7134_BOARD_RTD_VFG7330, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, + .subdevice = 0x1044, + .driver_data = SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1131, + .subdevice = 0x4ee9, + .driver_data = SAA7134_BOARD_MONSTERTV_MOBILE, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x11bd, + .subdevice = 0x002e, + .driver_data = SAA7134_BOARD_PINNACLE_PCTV_110i, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4862, + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2018, + .driver_data = SAA7134_BOARD_PHILIPS_TIGER, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1462, + .subdevice = 0x6231, /* tda8275a, ks003 IR */ + .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1462, + .subdevice = 0x8624, /* tda8275, ks003 IR */ + .driver_data = SAA7134_BOARD_MSI_TVATANYWHERE_PLUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x153b, + .subdevice = 0x1160, + .driver_data = SAA7134_BOARD_CINERGY250PCI, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA 7131E */ + .subvendor = 0x5168, + .subdevice = 0x0319, + .driver_data = SAA7134_BOARD_FLYDVB_TRIO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, + .subdevice = 0x2c05, + .driver_data = SAA7134_BOARD_AVERMEDIA_777, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x5168, + .subdevice = 0x0301, + .driver_data = SAA7134_BOARD_FLYDVBT_LR301, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0331, + .subdevice = 0x1421, + .driver_data = SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x17de, + .subdevice = 0x7201, + .driver_data = SAA7134_BOARD_TEVION_DVBT_220RF, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x17de, + .subdevice = 0x7250, + .driver_data = SAA7134_BOARD_KWORLD_DVBT_210, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ + .subvendor = 0x17de, + .subdevice = 0x7350, + .driver_data = SAA7134_BOARD_KWORLD_ATSC110, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ + .subvendor = 0x17de, + .subdevice = 0x7352, + .driver_data = SAA7134_BOARD_KWORLD_ATSC110, /* ATSC 115 */ + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ + .subvendor = 0x17de, + .subdevice = 0xa134, + .driver_data = SAA7134_BOARD_KWORLD_PC150U, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, + .subdevice = 0x7360, + .driver_data = SAA7134_BOARD_AVERMEDIA_A169_B, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, + .subdevice = 0x6360, + .driver_data = SAA7134_BOARD_AVERMEDIA_A169_B1, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x16be, + .subdevice = 0x0005, + .driver_data = SAA7134_BOARD_MD7134_BRIDGE_2, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x5168, + .subdevice = 0x0300, + .driver_data = SAA7134_BOARD_FLYDVBS_LR300, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x4e42, + .subdevice = 0x0300,/* LR300 */ + .driver_data = SAA7134_BOARD_FLYDVBS_LR300, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1489, + .subdevice = 0x0301, + .driver_data = SAA7134_BOARD_FLYDVBT_LR301, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, /* Animation Technologies (LifeView) */ + .subdevice = 0x0304, + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, + .subdevice = 0x3306, + .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, + .subdevice = 0x3502, /* whats the difference to 0x3306 ?*/ + .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5168, + .subdevice = 0x3307, /* FlyDVB-T Hybrid Mini PCI */ + .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x16be, + .subdevice = 0x0007, + .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x16be, + .subdevice = 0x0008, + .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x16be, + .subdevice = 0x000d, /* triple CTX948_V1.1.1 */ + .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, + .subdevice = 0x2c05, + .driver_data = SAA7134_BOARD_AVERMEDIA_777, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1489, + .subdevice = 0x0502, /* Cardbus version */ + .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x0919, /* Philips Proteus PRO 2309 */ + .subdevice = 0x2003, + .driver_data = SAA7134_BOARD_PROTEUS_2309, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, + .subdevice = 0x2c00, + .driver_data = SAA7134_BOARD_AVERMEDIA_A16AR, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1043, + .subdevice = 0x4860, + .driver_data = SAA7134_BOARD_ASUS_EUROPA2_HYBRID, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x11bd, + .subdevice = 0x002f, + .driver_data = SAA7134_BOARD_PINNACLE_PCTV_310i, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0x9715, + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa11b, + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507UA, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4876, + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6700, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6701, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6702, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6703, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6704, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6705, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6706, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6707, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6708, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x6709, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0070, + .subdevice = 0x670a, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x153b, + .subdevice = 0x1172, + .driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2342, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1131, + .subdevice = 0x2341, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x3016, + .subdevice = 0x2344, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1131, + .subdevice = 0x230f, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1a7f, + .subdevice = 0x2008, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM53, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1a7f, + .subdevice = 0x2108, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM3, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x153b, + .subdevice = 0x1175, + .driver_data = SAA7134_BOARD_CINERGY_HT_PCI, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf31e, + .driver_data = SAA7134_BOARD_AVERMEDIA_M102, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x4E42, /* MSI */ + .subdevice = 0x0306, /* TV@nywhere DUO */ + .driver_data = SAA7134_BOARD_FLYDVBTDUO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4871, + .driver_data = SAA7134_BOARD_ASUS_P7131_4871, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4857, /* REV:1.00 */ + .driver_data = SAA7134_BOARD_ASUSTeK_TIGER, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x0919, /* SinoVideo PCI 2309 Proteus (7134) */ + .subdevice = 0x2003, /* OEM cardbus */ + .driver_data = SAA7134_BOARD_SABRENT_TV_PCB05, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2304, + .driver_data = SAA7134_BOARD_10MOONSTVMASTER3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf01d, /* AVerTV DVB-T Super 007 */ + .driver_data = SAA7134_BOARD_AVERMEDIA_SUPER_007, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x0000, + .subdevice = 0x4016, + .driver_data = SAA7134_BOARD_BEHOLD_401, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x0000, + .subdevice = 0x4036, + .driver_data = SAA7134_BOARD_BEHOLD_403, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x0000, + .subdevice = 0x4037, + .driver_data = SAA7134_BOARD_BEHOLD_403FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x0000, + .subdevice = 0x4050, + .driver_data = SAA7134_BOARD_BEHOLD_405, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x0000, + .subdevice = 0x4051, + .driver_data = SAA7134_BOARD_BEHOLD_405FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x0000, + .subdevice = 0x4070, + .driver_data = SAA7134_BOARD_BEHOLD_407, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x0000, + .subdevice = 0x4071, + .driver_data = SAA7134_BOARD_BEHOLD_407FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0000, + .subdevice = 0x4090, + .driver_data = SAA7134_BOARD_BEHOLD_409, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x0000, + .subdevice = 0x505B, + .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK5, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x0000, + .subdevice = 0x5051, + .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x5ace, + .subdevice = 0x5050, + .driver_data = SAA7134_BOARD_BEHOLD_505FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0000, + .subdevice = 0x5071, + .driver_data = SAA7134_BOARD_BEHOLD_507RDS_MK3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0000, + .subdevice = 0x507B, + .driver_data = SAA7134_BOARD_BEHOLD_507RDS_MK5, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x5ace, + .subdevice = 0x5070, + .driver_data = SAA7134_BOARD_BEHOLD_507_9FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x5090, + .driver_data = SAA7134_BOARD_BEHOLD_507_9FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x0000, + .subdevice = 0x5201, + .driver_data = SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x5ace, + .subdevice = 0x6070, + .driver_data = SAA7134_BOARD_BEHOLD_607FM_MK3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x5ace, + .subdevice = 0x6071, + .driver_data = SAA7134_BOARD_BEHOLD_607FM_MK5, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x5ace, + .subdevice = 0x6072, + .driver_data = SAA7134_BOARD_BEHOLD_607RDS_MK3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x5ace, + .subdevice = 0x6073, + .driver_data = SAA7134_BOARD_BEHOLD_607RDS_MK5, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x6090, + .driver_data = SAA7134_BOARD_BEHOLD_609FM_MK3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x6091, + .driver_data = SAA7134_BOARD_BEHOLD_609FM_MK5, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x6092, + .driver_data = SAA7134_BOARD_BEHOLD_609RDS_MK3, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x6093, + .driver_data = SAA7134_BOARD_BEHOLD_609RDS_MK5, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x6190, + .driver_data = SAA7134_BOARD_BEHOLD_M6, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x6193, + .driver_data = SAA7134_BOARD_BEHOLD_M6_EXTRA, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x6191, + .driver_data = SAA7134_BOARD_BEHOLD_M63, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x4e42, + .subdevice = 0x3502, + .driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1822, /*Twinhan Technology Co. Ltd*/ + .subdevice = 0x0022, + .driver_data = SAA7134_BOARD_TWINHAN_DTV_DVB_3056, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x16be, + .subdevice = 0x0010, /* Medion version CTX953_V.1.4.3 */ + .driver_data = SAA7134_BOARD_CREATIX_CTX953, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1462, /* MSI */ + .subdevice = 0x8625, /* TV@nywhere A/D v1.1 */ + .driver_data = SAA7134_BOARD_MSI_TVANYWHERE_AD11, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf436, + .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS_506, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf936, + .driver_data = SAA7134_BOARD_AVERMEDIA_A16D, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa836, + .driver_data = SAA7134_BOARD_AVERMEDIA_M115, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x185b, + .subdevice = 0xc900, + .driver_data = SAA7134_BOARD_VIDEOMATE_T750, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */ + .subvendor = 0x1421, + .subdevice = 0x0380, + .driver_data = SAA7134_BOARD_ADS_INSTANT_HDTV_PCI, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5169, + .subdevice = 0x1502, + .driver_data = SAA7134_BOARD_FLYTVPLATINUM_MINI, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x6290, + .driver_data = SAA7134_BOARD_BEHOLD_H6, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf636, + .driver_data = SAA7134_BOARD_AVERMEDIA_M103, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf736, + .driver_data = SAA7134_BOARD_AVERMEDIA_M103, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4878, /* REV:1.02G */ + .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x48cd, + .driver_data = SAA7134_BOARD_ASUSTeK_PS3_100, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x17de, + .subdevice = 0x7128, + .driver_data = SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x17de, + .subdevice = 0xb136, + .driver_data = SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf31d, + .driver_data = SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x185b, + .subdevice = 0xc900, + .driver_data = SAA7134_BOARD_VIDEOMATE_S350, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ + .subdevice = 0x7595, + .driver_data = SAA7134_BOARD_BEHOLD_X7, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x19d1, /* RoverMedia */ + .subdevice = 0x0138, /* LifeView FlyTV Prime30 OEM */ + .driver_data = SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2004, + .driver_data = SAA7134_BOARD_ZOLID_HYBRID_PCI, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1043, + .subdevice = 0x4847, + .driver_data = SAA7134_BOARD_ASUS_EUROPA_HYBRID, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x107d, + .subdevice = 0x6655, + .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x13c2, + .subdevice = 0x2804, + .driver_data = SAA7134_BOARD_TECHNOTREND_BUDGET_T3000, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ + .subdevice = 0x7190, + .driver_data = SAA7134_BOARD_BEHOLD_H7, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, /* Beholder Intl. Ltd. */ + .subdevice = 0x7090, + .driver_data = SAA7134_BOARD_BEHOLD_A7, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7135, + .subvendor = 0x185b, + .subdevice = 0xc900, + .driver_data = SAA7134_BOARD_VIDEOMATE_M1F, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x5ace, + .subdevice = 0x5030, + .driver_data = SAA7134_BOARD_BEHOLD_503FM, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x5ace, + .subdevice = 0x5010, + .driver_data = SAA7134_BOARD_BEHOLD_501, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x17de, + .subdevice = 0xd136, + .driver_data = SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x6000, + .subdevice = 0x0811, + .driver_data = SAA7134_BOARD_SENSORAY811_911, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x6000, + .subdevice = 0x0911, + .driver_data = SAA7134_BOARD_SENSORAY811_911, + }, { + /* --- boards without eeprom + subsystem ID --- */ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0, + .driver_data = SAA7134_BOARD_NOAUTO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0, + .driver_data = SAA7134_BOARD_NOAUTO, + },{ + /* --- default catch --- */ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SAA7134_BOARD_UNKNOWN, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SAA7134_BOARD_UNKNOWN, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SAA7134_BOARD_UNKNOWN, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7135, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SAA7134_BOARD_UNKNOWN, + },{ + /* --- end of list --- */ + } +}; +MODULE_DEVICE_TABLE(pci, saa7134_pci_tbl); + +/* ----------------------------------------------------------- */ +/* flyvideo tweaks */ + + +static void board_flyvideo(struct saa7134_dev *dev) +{ + printk("%s: there are different flyvideo cards with different tuners\n" + "%s: out there, you might have to use the tuner=<nr> insmod\n" + "%s: option to override the default value.\n", + dev->name, dev->name, dev->name); +} + +static int saa7134_xc2028_callback(struct saa7134_dev *dev, + int command, int arg) +{ + switch (command) { + case XC2028_TUNER_RESET: + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00000000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000); + switch (dev->board) { + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + case SAA7134_BOARD_AVERMEDIA_M103: + saa7134_set_gpio(dev, 23, 0); + msleep(10); + saa7134_set_gpio(dev, 23, 1); + break; + case SAA7134_BOARD_AVERMEDIA_A16D: + saa7134_set_gpio(dev, 21, 0); + msleep(10); + saa7134_set_gpio(dev, 21, 1); + break; + case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: + saa7134_set_gpio(dev, 18, 0); + msleep(10); + saa7134_set_gpio(dev, 18, 1); + break; + case SAA7134_BOARD_VIDEOMATE_T750: + saa7134_set_gpio(dev, 20, 0); + msleep(10); + saa7134_set_gpio(dev, 20, 1); + break; + } + return 0; + } + return -EINVAL; +} + +static int saa7134_xc5000_callback(struct saa7134_dev *dev, + int command, int arg) +{ + switch (dev->board) { + case SAA7134_BOARD_BEHOLD_X7: + case SAA7134_BOARD_BEHOLD_H7: + case SAA7134_BOARD_BEHOLD_A7: + if (command == XC5000_TUNER_RESET) { + /* Down and UP pheripherial RESET pin for reset all chips */ + saa_writeb(SAA7134_SPECIAL_MODE, 0x00); + msleep(10); + saa_writeb(SAA7134_SPECIAL_MODE, 0x01); + msleep(10); + } + break; + default: + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x06e20000, 0x06e20000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x06a20000, 0x06a20000); + saa_andorl(SAA7133_ANALOG_IO_SELECT >> 2, 0x02, 0x02); + saa_andorl(SAA7134_ANALOG_IN_CTRL1 >> 2, 0x81, 0x81); + saa_andorl(SAA7134_AUDIO_CLOCK0 >> 2, 0x03187de7, 0x03187de7); + saa_andorl(SAA7134_AUDIO_PLL_CTRL >> 2, 0x03, 0x03); + saa_andorl(SAA7134_AUDIO_CLOCKS_PER_FIELD0 >> 2, + 0x0001e000, 0x0001e000); + break; + } + return 0; +} + +static int saa7134_tda8290_827x_callback(struct saa7134_dev *dev, + int command, int arg) +{ + u8 sync_control; + + switch (command) { + case 0: /* switch LNA gain through GPIO 22*/ + saa7134_set_gpio(dev, 22, arg) ; + break; + case 1: /* vsync output at GPIO22. 50 / 60Hz */ + saa_andorb(SAA7134_VIDEO_PORT_CTRL3, 0x80, 0x80); + saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x03); + if (arg == 1) + sync_control = 11; + else + sync_control = 17; + saa_writeb(SAA7134_VGATE_START, sync_control); + saa_writeb(SAA7134_VGATE_STOP, sync_control + 1); + saa_andorb(SAA7134_MISC_VGATE_MSB, 0x03, 0x00); + break; + default: + return -EINVAL; + } + + return 0; +} + +static inline int saa7134_tda18271_hvr11x0_toggle_agc(struct saa7134_dev *dev, + enum tda18271_mode mode) +{ + /* toggle AGC switch through GPIO 26 */ + switch (mode) { + case TDA18271_ANALOG: + saa7134_set_gpio(dev, 26, 0); + break; + case TDA18271_DIGITAL: + saa7134_set_gpio(dev, 26, 1); + break; + default: + return -EINVAL; + } + return 0; +} + +static inline int saa7134_kworld_sbtvd_toggle_agc(struct saa7134_dev *dev, + enum tda18271_mode mode) +{ + /* toggle AGC switch through GPIO 27 */ + switch (mode) { + case TDA18271_ANALOG: + saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x4000); + saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x4000); + msleep(20); + break; + case TDA18271_DIGITAL: + saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x14000); + saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x14000); + msleep(20); + saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x54000); + saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x54000); + msleep(30); + break; + default: + return -EINVAL; + } + return 0; +} + +static int saa7134_kworld_pc150u_toggle_agc(struct saa7134_dev *dev, + enum tda18271_mode mode) +{ + switch (mode) { + case TDA18271_ANALOG: + saa7134_set_gpio(dev, 18, 0); + break; + case TDA18271_DIGITAL: + saa7134_set_gpio(dev, 18, 1); + msleep(30); + break; + default: + return -EINVAL; + } + return 0; +} + +static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, + int command, int arg) +{ + int ret = 0; + + switch (command) { + case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */ + switch (dev->board) { + case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: + ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg); + break; + case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: + ret = saa7134_kworld_sbtvd_toggle_agc(dev, arg); + break; + case SAA7134_BOARD_KWORLD_PC150U: + ret = saa7134_kworld_pc150u_toggle_agc(dev, arg); + break; + default: + break; + } + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int saa7134_tda8290_callback(struct saa7134_dev *dev, + int command, int arg) +{ + int ret; + + switch (dev->board) { + case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_AVERMEDIA_M733A: + case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: + case SAA7134_BOARD_KWORLD_PC150U: + case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: + /* tda8290 + tda18271 */ + ret = saa7134_tda8290_18271_callback(dev, command, arg); + break; + default: + /* tda8290 + tda827x */ + ret = saa7134_tda8290_827x_callback(dev, command, arg); + break; + } + return ret; +} + +int saa7134_tuner_callback(void *priv, int component, int command, int arg) +{ + struct saa7134_dev *dev = priv; + + if (dev != NULL) { + switch (dev->tuner_type) { + case TUNER_PHILIPS_TDA8290: + return saa7134_tda8290_callback(dev, command, arg); + case TUNER_XC2028: + return saa7134_xc2028_callback(dev, command, arg); + case TUNER_XC5000: + return saa7134_xc5000_callback(dev, command, arg); + } + } else { + printk(KERN_ERR "saa7134: Error - device struct undefined.\n"); + return -EINVAL; + } + return -EINVAL; +} +EXPORT_SYMBOL(saa7134_tuner_callback); + +/* ----------------------------------------------------------- */ + +static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) +{ + struct tveeprom tv; + + tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data); + + /* Make sure we support the board model */ + switch (tv.model) { + case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */ + case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ + case 67201: /* WinTV-HVR1150 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ + case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ + case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ + case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ + case 67569: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM) */ + case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */ + case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ + case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ + case 67651: /* WinTV-HVR1150 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ + case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ + break; + default: + printk(KERN_WARNING "%s: warning: " + "unknown hauppauge model #%d\n", dev->name, tv.model); + break; + } + + printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n", + dev->name, tv.model); +} + +/* ----------------------------------------------------------- */ + +int saa7134_board_init1(struct saa7134_dev *dev) +{ + /* Always print gpio, often manufacturers encode tuner type and other info. */ + saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0); + dev->gpio_value = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); + printk(KERN_INFO "%s: board init: gpio is %x\n", dev->name, dev->gpio_value); + + switch (dev->board) { + case SAA7134_BOARD_FLYVIDEO2000: + case SAA7134_BOARD_FLYVIDEO3000: + case SAA7134_BOARD_FLYVIDEO3000_NTSC: + dev->has_remote = SAA7134_REMOTE_GPIO; + board_flyvideo(dev); + break; + case SAA7134_BOARD_FLYTVPLATINUM_MINI2: + case SAA7134_BOARD_FLYTVPLATINUM_FM: + case SAA7134_BOARD_CINERGY400: + case SAA7134_BOARD_CINERGY600: + case SAA7134_BOARD_CINERGY600_MK3: + case SAA7134_BOARD_ECS_TVP3XP: + case SAA7134_BOARD_ECS_TVP3XP_4CB5: + case SAA7134_BOARD_ECS_TVP3XP_4CB6: + case SAA7134_BOARD_MD2819: + case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: + case SAA7134_BOARD_KWORLD_XPERT: + case SAA7134_BOARD_AVERMEDIA_STUDIO_305: + case SAA7134_BOARD_AVERMEDIA_STUDIO_505: + case SAA7134_BOARD_AVERMEDIA_305: + case SAA7134_BOARD_AVERMEDIA_STUDIO_307: + case SAA7134_BOARD_AVERMEDIA_307: + case SAA7134_BOARD_AVERMEDIA_STUDIO_507: + case SAA7134_BOARD_AVERMEDIA_GO_007_FM: + case SAA7134_BOARD_AVERMEDIA_777: + case SAA7134_BOARD_AVERMEDIA_M135A: +/* case SAA7134_BOARD_SABRENT_SBTTVFM: */ /* not finished yet */ + case SAA7134_BOARD_VIDEOMATE_TV_PVR: + case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS: + case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII: + case SAA7134_BOARD_VIDEOMATE_M1F: + case SAA7134_BOARD_VIDEOMATE_DVBT_300: + case SAA7134_BOARD_VIDEOMATE_DVBT_200: + case SAA7134_BOARD_VIDEOMATE_DVBT_200A: + case SAA7134_BOARD_MANLI_MTV001: + case SAA7134_BOARD_MANLI_MTV002: + case SAA7134_BOARD_BEHOLD_409FM: + case SAA7134_BOARD_AVACSSMARTTV: + case SAA7134_BOARD_GOTVIEW_7135: + case SAA7134_BOARD_KWORLD_TERMINATOR: + case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: + case SAA7134_BOARD_FLYDVBT_LR301: + case SAA7134_BOARD_ASUSTeK_PS3_100: + case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: + case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: + case SAA7134_BOARD_FLYDVBTDUO: + case SAA7134_BOARD_PROTEUS_2309: + case SAA7134_BOARD_AVERMEDIA_A16AR: + case SAA7134_BOARD_ENCORE_ENLTV: + case SAA7134_BOARD_ENCORE_ENLTV_FM: + case SAA7134_BOARD_ENCORE_ENLTV_FM53: + case SAA7134_BOARD_ENCORE_ENLTV_FM3: + case SAA7134_BOARD_10MOONSTVMASTER3: + case SAA7134_BOARD_BEHOLD_401: + case SAA7134_BOARD_BEHOLD_403: + case SAA7134_BOARD_BEHOLD_403FM: + case SAA7134_BOARD_BEHOLD_405: + case SAA7134_BOARD_BEHOLD_405FM: + case SAA7134_BOARD_BEHOLD_407: + case SAA7134_BOARD_BEHOLD_407FM: + case SAA7134_BOARD_BEHOLD_409: + case SAA7134_BOARD_BEHOLD_505FM: + case SAA7134_BOARD_BEHOLD_505RDS_MK5: + case SAA7134_BOARD_BEHOLD_505RDS_MK3: + case SAA7134_BOARD_BEHOLD_507_9FM: + case SAA7134_BOARD_BEHOLD_507RDS_MK3: + case SAA7134_BOARD_BEHOLD_507RDS_MK5: + case SAA7134_BOARD_GENIUS_TVGO_A11MCE: + case SAA7134_BOARD_REAL_ANGEL_220: + case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: + case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: + case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: + case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: + dev->has_remote = SAA7134_REMOTE_GPIO; + break; + case SAA7134_BOARD_FLYDVBS_LR300: + saa_writeb(SAA7134_GPIO_GPMODE3, 0x80); + saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x40); + dev->has_remote = SAA7134_REMOTE_GPIO; + break; + case SAA7134_BOARD_MD5044: + printk("%s: seems there are two different versions of the MD5044\n" + "%s: (with the same ID) out there. If sound doesn't work for\n" + "%s: you try the audio_clock_override=0x200000 insmod option.\n", + dev->name,dev->name,dev->name); + break; + case SAA7134_BOARD_CINERGY400_CARDBUS: + /* power-up tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00040000, 0x00040000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00040000, 0x00000000); + break; + case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL: + /* this turns the remote control chip off to work around a bug in it */ + saa_writeb(SAA7134_GPIO_GPMODE1, 0x80); + saa_writeb(SAA7134_GPIO_GPSTATUS1, 0x80); + break; + case SAA7134_BOARD_MONSTERTV_MOBILE: + /* power-up tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00040000, 0x00040000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00040000, 0x00000004); + break; + case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: + /* turn the fan on */ + saa_writeb(SAA7134_GPIO_GPMODE3, 0x08); + saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x06); + break; + case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: + case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08000000, 0x08000000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000); + break; + case SAA7134_BOARD_AVERMEDIA_CARDBUS: + case SAA7134_BOARD_AVERMEDIA_M115: + /* power-down tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0); + msleep(10); + /* power-up tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0xffffffff, 0xffffffff); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0xffffffff); + msleep(10); + break; + case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: + /* power-down tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08400000, 0x08400000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08400000, 0); + msleep(10); + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08400000, 0x08400000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08400000, 0x08400000); + msleep(10); + dev->has_remote = SAA7134_REMOTE_I2C; + break; + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + saa7134_set_gpio(dev, 23, 0); + msleep(10); + saa7134_set_gpio(dev, 23, 1); + dev->has_remote = SAA7134_REMOTE_I2C; + break; + case SAA7134_BOARD_AVERMEDIA_M103: + saa7134_set_gpio(dev, 23, 0); + msleep(10); + saa7134_set_gpio(dev, 23, 1); + break; + case SAA7134_BOARD_AVERMEDIA_A16D: + saa7134_set_gpio(dev, 21, 0); + msleep(10); + saa7134_set_gpio(dev, 21, 1); + msleep(1); + dev->has_remote = SAA7134_REMOTE_GPIO; + break; + case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: + /* power-down tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0); + msleep(10); + /* power-up tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x000A8004, 0x000A8004); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x000A8004, 0x000A8004); + msleep(10); + /* remote via GPIO */ + dev->has_remote = SAA7134_REMOTE_GPIO; + break; + case SAA7134_BOARD_RTD_VFG7350: + + /* + * Make sure Production Test Register at offset 0x1D1 is cleared + * to take chip out of test mode. Clearing bit 4 (TST_EN_AOUT) + * prevents pin 105 from remaining low; keeping pin 105 low + * continually resets the SAA6752 chip. + */ + + saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00); + break; + case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + dev->has_remote = SAA7134_REMOTE_GPIO; + /* GPIO 26 high for digital, low for analog */ + saa7134_set_gpio(dev, 26, 0); + msleep(1); + + saa7134_set_gpio(dev, 22, 0); + msleep(10); + saa7134_set_gpio(dev, 22, 1); + break; + /* i2c remotes */ + case SAA7134_BOARD_PINNACLE_PCTV_110i: + case SAA7134_BOARD_PINNACLE_PCTV_310i: + case SAA7134_BOARD_UPMOST_PURPLE_TV: + case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: + case SAA7134_BOARD_HAUPPAUGE_HVR1110: + case SAA7134_BOARD_BEHOLD_607FM_MK3: + case SAA7134_BOARD_BEHOLD_607FM_MK5: + case SAA7134_BOARD_BEHOLD_609FM_MK3: + case SAA7134_BOARD_BEHOLD_609FM_MK5: + case SAA7134_BOARD_BEHOLD_607RDS_MK3: + case SAA7134_BOARD_BEHOLD_607RDS_MK5: + case SAA7134_BOARD_BEHOLD_609RDS_MK3: + case SAA7134_BOARD_BEHOLD_609RDS_MK5: + case SAA7134_BOARD_BEHOLD_M6: + case SAA7134_BOARD_BEHOLD_M63: + case SAA7134_BOARD_BEHOLD_M6_EXTRA: + case SAA7134_BOARD_BEHOLD_H6: + case SAA7134_BOARD_BEHOLD_X7: + case SAA7134_BOARD_BEHOLD_H7: + case SAA7134_BOARD_BEHOLD_A7: + case SAA7134_BOARD_KWORLD_PC150U: + dev->has_remote = SAA7134_REMOTE_I2C; + break; + case SAA7134_BOARD_AVERMEDIA_A169_B: + printk("%s: %s: dual saa713x broadcast decoders\n" + "%s: Sorry, none of the inputs to this chip are supported yet.\n" + "%s: Dual decoder functionality is disabled for now, use the other chip.\n", + dev->name,card(dev).name,dev->name,dev->name); + break; + case SAA7134_BOARD_AVERMEDIA_M102: + /* enable tuner */ + dev->has_remote = SAA7134_REMOTE_GPIO; + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x8c040007, 0x8c040007); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd); + break; + case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: + case SAA7134_BOARD_AVERMEDIA_A700_PRO: + /* write windows gpio values */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100); + break; + case SAA7134_BOARD_VIDEOMATE_S350: + dev->has_remote = SAA7134_REMOTE_GPIO; + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0000C000, 0x0000C000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0000C000, 0x0000C000); + break; + case SAA7134_BOARD_AVERMEDIA_M733A: + saa7134_set_gpio(dev, 1, 1); + msleep(10); + saa7134_set_gpio(dev, 1, 0); + msleep(10); + saa7134_set_gpio(dev, 1, 1); + dev->has_remote = SAA7134_REMOTE_GPIO; + break; + case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: + /* enable LGS-8G75 */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0e050000, 0x0c050000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0e050000, 0x0c050000); + break; + case SAA7134_BOARD_VIDEOMATE_T750: + /* enable the analog tuner */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00008000, 0x00008000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000); + break; + } + return 0; +} + +static void saa7134_tuner_setup(struct saa7134_dev *dev) +{ + struct tuner_setup tun_setup; + unsigned int mode_mask = T_RADIO | T_ANALOG_TV; + + memset(&tun_setup, 0, sizeof(tun_setup)); + tun_setup.tuner_callback = saa7134_tuner_callback; + + if (saa7134_boards[dev->board].radio_type != UNSET) { + tun_setup.type = saa7134_boards[dev->board].radio_type; + tun_setup.addr = saa7134_boards[dev->board].radio_addr; + + tun_setup.mode_mask = T_RADIO; + + saa_call_all(dev, tuner, s_type_addr, &tun_setup); + mode_mask &= ~T_RADIO; + } + + if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type != UNSET)) { + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + tun_setup.config = saa7134_boards[dev->board].tuner_config; + tun_setup.tuner_callback = saa7134_tuner_callback; + + tun_setup.mode_mask = mode_mask; + + saa_call_all(dev, tuner, s_type_addr, &tun_setup); + } + + if (dev->tda9887_conf) { + struct v4l2_priv_tun_config tda9887_cfg; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &dev->tda9887_conf; + + saa_call_all(dev, tuner, s_config, &tda9887_cfg); + } + + if (dev->tuner_type == TUNER_XC2028) { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); + memset(&ctl, 0, sizeof(ctl)); + + ctl.fname = XC2028_DEFAULT_FIRMWARE; + ctl.max_len = 64; + + switch (dev->board) { + case SAA7134_BOARD_AVERMEDIA_A16D: + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + case SAA7134_BOARD_AVERMEDIA_M103: + case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: + ctl.demod = XC3028_FE_ZARLINK456; + break; + default: + ctl.demod = XC3028_FE_OREN538; + ctl.mts = 1; + } + + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + + saa_call_all(dev, tuner, s_config, &xc2028_cfg); + } +} + +/* stuff which needs working i2c */ +int saa7134_board_init2(struct saa7134_dev *dev) +{ + unsigned char buf; + int board; + + /* Put here the code that enables the chips that are needed + for analog mode and doesn't depend on the tuner attachment. + It is also a good idea to get tuner type from eeprom, etc before + initializing tuner, since we can avoid loading tuner driver + on devices that has TUNER_ABSENT + */ + switch (dev->board) { + case SAA7134_BOARD_BMK_MPEX_NOTUNER: + case SAA7134_BOARD_BMK_MPEX_TUNER: + /* Checks if the device has a tuner at 0x60 addr + If the device doesn't have a tuner, TUNER_ABSENT + will be used at tuner_type, avoiding loading tuner + without needing it + */ + dev->i2c_client.addr = 0x60; + board = (i2c_master_recv(&dev->i2c_client, &buf, 0) < 0) + ? SAA7134_BOARD_BMK_MPEX_NOTUNER + : SAA7134_BOARD_BMK_MPEX_TUNER; + if (board == dev->board) + break; + dev->board = board; + printk("%s: board type fixup: %s\n", dev->name, + saa7134_boards[dev->board].name); + dev->tuner_type = saa7134_boards[dev->board].tuner_type; + + break; + case SAA7134_BOARD_MD7134: + { + u8 subaddr; + u8 data[3]; + int ret, tuner_t; + struct i2c_msg msg[] = {{.addr=0x50, .flags=0, .buf=&subaddr, .len = 1}, + {.addr=0x50, .flags=I2C_M_RD, .buf=data, .len = 3}}; + + subaddr= 0x14; + tuner_t = 0; + + /* Retrieve device data from eeprom, checking for the + proper tuner_type. + */ + ret = i2c_transfer(&dev->i2c_adap, msg, 2); + if (ret != 2) { + printk(KERN_ERR "EEPROM read failure\n"); + } else if ((data[0] != 0) && (data[0] != 0xff)) { + /* old config structure */ + subaddr = data[0] + 2; + msg[1].len = 2; + i2c_transfer(&dev->i2c_adap, msg, 2); + tuner_t = (data[0] << 8) + data[1]; + switch (tuner_t){ + case 0x0103: + dev->tuner_type = TUNER_PHILIPS_PAL; + break; + case 0x010C: + dev->tuner_type = TUNER_PHILIPS_FM1216ME_MK3; + break; + default: + printk(KERN_ERR "%s Can't determine tuner type %x from EEPROM\n", dev->name, tuner_t); + } + } else if ((data[1] != 0) && (data[1] != 0xff)) { + /* new config structure */ + subaddr = data[1] + 1; + msg[1].len = 1; + i2c_transfer(&dev->i2c_adap, msg, 2); + subaddr = data[0] + 1; + msg[1].len = 2; + i2c_transfer(&dev->i2c_adap, msg, 2); + tuner_t = (data[1] << 8) + data[0]; + switch (tuner_t) { + case 0x0005: + dev->tuner_type = TUNER_PHILIPS_FM1216ME_MK3; + break; + case 0x001d: + dev->tuner_type = TUNER_PHILIPS_FMD1216ME_MK3; + printk(KERN_INFO "%s Board has DVB-T\n", dev->name); + break; + default: + printk(KERN_ERR "%s Can't determine tuner type %x from EEPROM\n", dev->name, tuner_t); + } + } else { + printk(KERN_ERR "%s unexpected config structure\n", dev->name); + } + + printk(KERN_INFO "%s Tuner type is %d\n", dev->name, dev->tuner_type); + break; + } + case SAA7134_BOARD_PHILIPS_EUROPA: + if (dev->autodetected && (dev->eedata[0x41] == 0x1c)) { + /* Reconfigure board as Snake reference design */ + dev->board = SAA7134_BOARD_PHILIPS_SNAKE; + dev->tuner_type = saa7134_boards[dev->board].tuner_type; + printk(KERN_INFO "%s: Reconfigured board as %s\n", + dev->name, saa7134_boards[dev->board].name); + break; + } + /* break intentionally omitted */ + case SAA7134_BOARD_VIDEOMATE_DVBT_300: + case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: + case SAA7134_BOARD_ASUS_EUROPA_HYBRID: + case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000: + { + + /* The Philips EUROPA based hybrid boards have the tuner + connected through the channel decoder. We have to make it + transparent to find it + */ + u8 data[] = { 0x07, 0x02}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + i2c_transfer(&dev->i2c_adap, &msg, 1); + + break; + } + case SAA7134_BOARD_PHILIPS_TIGER: + case SAA7134_BOARD_PHILIPS_TIGER_S: + { + u8 data[] = { 0x3c, 0x33, 0x60}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + if (dev->autodetected && (dev->eedata[0x49] == 0x50)) { + dev->board = SAA7134_BOARD_PHILIPS_TIGER_S; + printk(KERN_INFO "%s: Reconfigured board as %s\n", + dev->name, saa7134_boards[dev->board].name); + } + if (dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) { + dev->tuner_type = TUNER_PHILIPS_TDA8290; + + data[2] = 0x68; + i2c_transfer(&dev->i2c_adap, &msg, 1); + break; + } + i2c_transfer(&dev->i2c_adap, &msg, 1); + break; + } + case SAA7134_BOARD_ASUSTeK_TVFM7135: + /* The card below is detected as card=53, but is different */ + if (dev->autodetected && (dev->eedata[0x27] == 0x03)) { + dev->board = SAA7134_BOARD_ASUSTeK_P7131_ANALOG; + printk(KERN_INFO "%s: P7131 analog only, using " + "entry of %s\n", + dev->name, saa7134_boards[dev->board].name); + + /* IR init has already happened for other cards, so + * we have to catch up. */ + dev->has_remote = SAA7134_REMOTE_GPIO; + saa7134_input_init1(dev); + } + break; + case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + hauppauge_eeprom(dev, dev->eedata+0x80); + break; + case SAA7134_BOARD_HAUPPAUGE_HVR1110: + hauppauge_eeprom(dev, dev->eedata+0x80); + /* break intentionally omitted */ + case SAA7134_BOARD_PINNACLE_PCTV_310i: + case SAA7134_BOARD_KWORLD_DVBT_210: + case SAA7134_BOARD_TEVION_DVBT_220RF: + case SAA7134_BOARD_ASUSTeK_TIGER: + case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: + case SAA7134_BOARD_MEDION_MD8800_QUADRO: + case SAA7134_BOARD_AVERMEDIA_SUPER_007: + case SAA7134_BOARD_TWINHAN_DTV_DVB_3056: + case SAA7134_BOARD_CREATIX_CTX953: + { + /* this is a hybrid board, initialize to analog mode + * and configure firmware eeprom address + */ + u8 data[] = { 0x3c, 0x33, 0x60}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + i2c_transfer(&dev->i2c_adap, &msg, 1); + break; + } + case SAA7134_BOARD_ASUSTeK_TIGER_3IN1: + { + u8 data[] = { 0x3c, 0x33, 0x60}; + struct i2c_msg msg = {.addr = 0x0b, .flags = 0, .buf = data, + .len = sizeof(data)}; + i2c_transfer(&dev->i2c_adap, &msg, 1); + break; + } + case SAA7134_BOARD_ASUSTeK_PS3_100: + { + u8 data[] = { 0x3c, 0x33, 0x60}; + struct i2c_msg msg = {.addr = 0x0b, .flags = 0, .buf = data, + .len = sizeof(data)}; + i2c_transfer(&dev->i2c_adap, &msg, 1); + break; + } + case SAA7134_BOARD_FLYDVB_TRIO: + { + u8 temp = 0; + int rc; + u8 data[] = { 0x3c, 0x33, 0x62}; + struct i2c_msg msg = {.addr=0x09, .flags=0, .buf=data, .len = sizeof(data)}; + i2c_transfer(&dev->i2c_adap, &msg, 1); + + /* + * send weak up message to pic16C505 chip + * @ LifeView FlyDVB Trio + */ + msg.buf = &temp; + msg.addr = 0x0b; + msg.len = 1; + if (1 != i2c_transfer(&dev->i2c_adap, &msg, 1)) { + printk(KERN_WARNING "%s: send wake up byte to pic16C505" + "(IR chip) failed\n", dev->name); + } else { + msg.flags = I2C_M_RD; + rc = i2c_transfer(&dev->i2c_adap, &msg, 1); + printk(KERN_INFO "%s: probe IR chip @ i2c 0x%02x: %s\n", + dev->name, msg.addr, + (1 == rc) ? "yes" : "no"); + if (rc == 1) + dev->has_remote = SAA7134_REMOTE_I2C; + } + break; + } + case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: + case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: + { + /* initialize analog mode */ + u8 data[] = { 0x3c, 0x33, 0x6a}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + i2c_transfer(&dev->i2c_adap, &msg, 1); + break; + } + case SAA7134_BOARD_CINERGY_HT_PCMCIA: + case SAA7134_BOARD_CINERGY_HT_PCI: + { + /* initialize analog mode */ + u8 data[] = { 0x3c, 0x33, 0x68}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + i2c_transfer(&dev->i2c_adap, &msg, 1); + break; + } + case SAA7134_BOARD_VIDEOMATE_DVBT_200: + case SAA7134_BOARD_VIDEOMATE_DVBT_200A: + /* The T200 and the T200A share the same pci id. Consequently, + * we are going to query eeprom to try to find out which one we + * are actually looking at. */ + + /* Don't do this if the board was specifically selected with an + * insmod option or if we have the default configuration T200*/ + if (!dev->autodetected || (dev->eedata[0x41] == 0xd0)) + break; + if (dev->eedata[0x41] == 0x02) { + /* Reconfigure board as T200A */ + dev->board = SAA7134_BOARD_VIDEOMATE_DVBT_200A; + dev->tuner_type = saa7134_boards[dev->board].tuner_type; + dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; + printk(KERN_INFO "%s: Reconfigured board as %s\n", + dev->name, saa7134_boards[dev->board].name); + } else { + printk(KERN_WARNING "%s: Unexpected tuner type info: %x in eeprom\n", + dev->name, dev->eedata[0x41]); + break; + } + break; + case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI: + case SAA7134_BOARD_KWORLD_ATSC110: + { + struct i2c_msg msg = { .addr = 0x0a, .flags = 0 }; + int i; + static u8 buffer[][2] = { + { 0x10, 0x12 }, + { 0x13, 0x04 }, + { 0x16, 0x00 }, + { 0x14, 0x04 }, + { 0x17, 0x00 }, + }; + + for (i = 0; i < ARRAY_SIZE(buffer); i++) { + msg.buf = &buffer[i][0]; + msg.len = ARRAY_SIZE(buffer[0]); + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) + printk(KERN_WARNING + "%s: Unable to enable tuner(%i).\n", + dev->name, i); + } + break; + } + case SAA7134_BOARD_BEHOLD_H6: + { + u8 data[] = { 0x09, 0x9f, 0x86, 0x11}; + struct i2c_msg msg = {.addr = 0x61, .flags = 0, .buf = data, + .len = sizeof(data)}; + + /* The tuner TUNER_PHILIPS_FMD1216MEX_MK3 after hardware */ + /* start has disabled IF and enabled DVB-T. When saa7134 */ + /* scan I2C devices it not detect IF tda9887 and can`t */ + /* watch TV without software reboot. For solve this problem */ + /* switch the tuner to analog TV mode manually. */ + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) + printk(KERN_WARNING + "%s: Unable to enable IF of the tuner.\n", + dev->name); + break; + } + case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: + saa_writel(SAA7134_GPIO_GPMODE0 >> 2, 0x4000); + saa_writel(SAA7134_GPIO_GPSTATUS0 >> 2, 0x4000); + + saa7134_set_gpio(dev, 27, 0); + break; + } /* switch() */ + + /* initialize tuner */ + if (TUNER_ABSENT != dev->tuner_type) { + int has_demod = (dev->tda9887_conf & TDA9887_PRESENT); + + /* Note: radio tuner address is always filled in, + so we do not need to probe for a radio tuner device. */ + if (dev->radio_type != UNSET) + v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "tuner", + dev->radio_addr, NULL); + if (has_demod) + v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "tuner", + 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + if (dev->tuner_addr == ADDR_UNSET) { + enum v4l2_i2c_tuner_type type = + has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; + + v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "tuner", + 0, v4l2_i2c_tuner_addrs(type)); + } else { + v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "tuner", + dev->tuner_addr, NULL); + } + } + + saa7134_tuner_setup(dev); + + switch (dev->board) { + case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: + case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: + { + struct v4l2_priv_tun_config tea5767_cfg; + struct tea5767_ctrl ctl; + + dev->i2c_client.addr = 0xC0; + /* set TEA5767(analog FM) defines */ + memset(&ctl, 0, sizeof(ctl)); + ctl.xtal_freq = TEA5767_HIGH_LO_13MHz; + tea5767_cfg.tuner = TUNER_TEA5767; + tea5767_cfg.priv = &ctl; + saa_call_all(dev, tuner, s_config, &tea5767_cfg); + break; + } + } /* switch() */ + + return 0; +} diff --git a/drivers/media/pci/saa7134/saa7134-core.c b/drivers/media/pci/saa7134/saa7134-core.c new file mode 100644 index 000000000000..f2b37e05b964 --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-core.c @@ -0,0 +1,1368 @@ +/* + * + * device driver for philips saa7134 based TV cards + * driver core + * + * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/kmod.h> +#include <linux/sound.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/mutex.h> +#include <linux/dma-mapping.h> +#include <linux/pm.h> + +#include "saa7134-reg.h" +#include "saa7134.h" + +MODULE_DESCRIPTION("v4l2 driver module for saa7130/34 based TV cards"); +MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(SAA7134_VERSION); + + +/* ------------------------------------------------------------------ */ + +static unsigned int irq_debug; +module_param(irq_debug, int, 0644); +MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]"); + +static unsigned int core_debug; +module_param(core_debug, int, 0644); +MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); + +static unsigned int gpio_tracking; +module_param(gpio_tracking, int, 0644); +MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]"); + +static unsigned int alsa = 1; +module_param(alsa, int, 0644); +MODULE_PARM_DESC(alsa,"enable/disable ALSA DMA sound [dmasound]"); + +static unsigned int latency = UNSET; +module_param(latency, int, 0444); +MODULE_PARM_DESC(latency,"pci latency timer"); + +int saa7134_no_overlay=-1; +module_param_named(no_overlay, saa7134_no_overlay, int, 0444); +MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)" + " [some VIA/SIS chipsets are known to have problem with overlay]"); + +static unsigned int video_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; +static unsigned int vbi_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; +static unsigned int radio_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; +static unsigned int tuner[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; +static unsigned int card[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; + + +module_param_array(video_nr, int, NULL, 0444); +module_param_array(vbi_nr, int, NULL, 0444); +module_param_array(radio_nr, int, NULL, 0444); +module_param_array(tuner, int, NULL, 0444); +module_param_array(card, int, NULL, 0444); + +MODULE_PARM_DESC(video_nr, "video device number"); +MODULE_PARM_DESC(vbi_nr, "vbi device number"); +MODULE_PARM_DESC(radio_nr, "radio device number"); +MODULE_PARM_DESC(tuner, "tuner type"); +MODULE_PARM_DESC(card, "card type"); + +DEFINE_MUTEX(saa7134_devlist_lock); +EXPORT_SYMBOL(saa7134_devlist_lock); +LIST_HEAD(saa7134_devlist); +EXPORT_SYMBOL(saa7134_devlist); +static LIST_HEAD(mops_list); +static unsigned int saa7134_devcount; + +int (*saa7134_dmasound_init)(struct saa7134_dev *dev); +int (*saa7134_dmasound_exit)(struct saa7134_dev *dev); + +#define dprintk(fmt, arg...) if (core_debug) \ + printk(KERN_DEBUG "%s/core: " fmt, dev->name , ## arg) + +void saa7134_track_gpio(struct saa7134_dev *dev, char *msg) +{ + unsigned long mode,status; + + if (!gpio_tracking) + return; + /* rising SAA7134_GPIO_GPRESCAN reads the status */ + saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,0); + saa_andorb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN,SAA7134_GPIO_GPRESCAN); + mode = saa_readl(SAA7134_GPIO_GPMODE0 >> 2) & 0xfffffff; + status = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & 0xfffffff; + printk(KERN_DEBUG + "%s: gpio: mode=0x%07lx in=0x%07lx out=0x%07lx [%s]\n", + dev->name, mode, (~mode) & status, mode & status, msg); +} + +void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value) +{ + u32 index, bitval; + + index = 1 << bit_no; + switch (value) { + case 0: /* static value */ + case 1: dprintk("setting GPIO%d to static %d\n", bit_no, value); + /* turn sync mode off if necessary */ + if (index & 0x00c00000) + saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x00); + if (value) + bitval = index; + else + bitval = 0; + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, index); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, index, bitval); + break; + case 3: /* tristate */ + dprintk("setting GPIO%d to tristate\n", bit_no); + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, 0); + break; + } +} + +/* ------------------------------------------------------------------ */ + + +/* ----------------------------------------------------------- */ +/* delayed request_module */ + +#if defined(CONFIG_MODULES) && defined(MODULE) + +static void request_module_async(struct work_struct *work){ + struct saa7134_dev* dev = container_of(work, struct saa7134_dev, request_module_wk); + if (card_is_empress(dev)) + request_module("saa7134-empress"); + if (card_is_dvb(dev)) + request_module("saa7134-dvb"); + if (alsa) { + if (dev->pci->device != PCI_DEVICE_ID_PHILIPS_SAA7130) + request_module("saa7134-alsa"); + } +} + +static void request_submodules(struct saa7134_dev *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} + +static void flush_request_submodules(struct saa7134_dev *dev) +{ + flush_work(&dev->request_module_wk); +} + +#else +#define request_submodules(dev) +#define flush_request_submodules(dev) +#endif /* CONFIG_MODULES */ + +/* ------------------------------------------------------------------ */ + +/* nr of (saa7134-)pages for the given buffer size */ +static int saa7134_buffer_pages(int size) +{ + size = PAGE_ALIGN(size); + size += PAGE_SIZE; /* for non-page-aligned buffers */ + size /= 4096; + return size; +} + +/* calc max # of buffers from size (must not exceed the 4MB virtual + * address space per DMA channel) */ +int saa7134_buffer_count(unsigned int size, unsigned int count) +{ + unsigned int maxcount; + + maxcount = 1024 / saa7134_buffer_pages(size); + if (count > maxcount) + count = maxcount; + return count; +} + +int saa7134_buffer_startpage(struct saa7134_buf *buf) +{ + return saa7134_buffer_pages(buf->vb.bsize) * buf->vb.i; +} + +unsigned long saa7134_buffer_base(struct saa7134_buf *buf) +{ + unsigned long base; + struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + + base = saa7134_buffer_startpage(buf) * 4096; + base += dma->sglist[0].offset; + return base; +} + +/* ------------------------------------------------------------------ */ + +int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt) +{ + __le32 *cpu; + dma_addr_t dma_addr = 0; + + cpu = pci_alloc_consistent(pci, SAA7134_PGTABLE_SIZE, &dma_addr); + if (NULL == cpu) + return -ENOMEM; + pt->size = SAA7134_PGTABLE_SIZE; + pt->cpu = cpu; + pt->dma = dma_addr; + return 0; +} + +int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt, + struct scatterlist *list, unsigned int length, + unsigned int startpage) +{ + __le32 *ptr; + unsigned int i,p; + + BUG_ON(NULL == pt || NULL == pt->cpu); + + ptr = pt->cpu + startpage; + for (i = 0; i < length; i++, list++) + for (p = 0; p * 4096 < list->length; p++, ptr++) + *ptr = cpu_to_le32(sg_dma_address(list) - list->offset); + return 0; +} + +void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt) +{ + if (NULL == pt->cpu) + return; + pci_free_consistent(pci, pt->size, pt->cpu, pt->dma); + pt->cpu = NULL; +} + +/* ------------------------------------------------------------------ */ + +void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf) +{ + struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + BUG_ON(in_interrupt()); + + videobuf_waiton(q, &buf->vb, 0, 0); + videobuf_dma_unmap(q->dev, dma); + videobuf_dma_free(dma); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +/* ------------------------------------------------------------------ */ + +int saa7134_buffer_queue(struct saa7134_dev *dev, + struct saa7134_dmaqueue *q, + struct saa7134_buf *buf) +{ + struct saa7134_buf *next = NULL; + + assert_spin_locked(&dev->slock); + dprintk("buffer_queue %p\n",buf); + if (NULL == q->curr) { + if (!q->need_two) { + q->curr = buf; + buf->activate(dev,buf,NULL); + } else if (list_empty(&q->queue)) { + list_add_tail(&buf->vb.queue,&q->queue); + buf->vb.state = VIDEOBUF_QUEUED; + } else { + next = list_entry(q->queue.next,struct saa7134_buf, + vb.queue); + q->curr = buf; + buf->activate(dev,buf,next); + } + } else { + list_add_tail(&buf->vb.queue,&q->queue); + buf->vb.state = VIDEOBUF_QUEUED; + } + return 0; +} + +void saa7134_buffer_finish(struct saa7134_dev *dev, + struct saa7134_dmaqueue *q, + unsigned int state) +{ + assert_spin_locked(&dev->slock); + dprintk("buffer_finish %p\n",q->curr); + + /* finish current buffer */ + q->curr->vb.state = state; + do_gettimeofday(&q->curr->vb.ts); + wake_up(&q->curr->vb.done); + q->curr = NULL; +} + +void saa7134_buffer_next(struct saa7134_dev *dev, + struct saa7134_dmaqueue *q) +{ + struct saa7134_buf *buf,*next = NULL; + + assert_spin_locked(&dev->slock); + BUG_ON(NULL != q->curr); + + if (!list_empty(&q->queue)) { + /* activate next one from queue */ + buf = list_entry(q->queue.next,struct saa7134_buf,vb.queue); + dprintk("buffer_next %p [prev=%p/next=%p]\n", + buf,q->queue.prev,q->queue.next); + list_del(&buf->vb.queue); + if (!list_empty(&q->queue)) + next = list_entry(q->queue.next,struct saa7134_buf, + vb.queue); + q->curr = buf; + buf->activate(dev,buf,next); + dprintk("buffer_next #2 prev=%p/next=%p\n", + q->queue.prev,q->queue.next); + } else { + /* nothing to do -- just stop DMA */ + dprintk("buffer_next %p\n",NULL); + saa7134_set_dmabits(dev); + del_timer(&q->timeout); + + if (card_has_mpeg(dev)) + if (dev->ts_started) + saa7134_ts_stop(dev); + } +} + +void saa7134_buffer_timeout(unsigned long data) +{ + struct saa7134_dmaqueue *q = (struct saa7134_dmaqueue*)data; + struct saa7134_dev *dev = q->dev; + unsigned long flags; + + spin_lock_irqsave(&dev->slock,flags); + + /* try to reset the hardware (SWRST) */ + saa_writeb(SAA7134_REGION_ENABLE, 0x00); + saa_writeb(SAA7134_REGION_ENABLE, 0x80); + saa_writeb(SAA7134_REGION_ENABLE, 0x00); + + /* flag current buffer as failed, + try to start over with the next one. */ + if (q->curr) { + dprintk("timeout on %p\n",q->curr); + saa7134_buffer_finish(dev,q,VIDEOBUF_ERROR); + } + saa7134_buffer_next(dev,q); + spin_unlock_irqrestore(&dev->slock,flags); +} + +/* ------------------------------------------------------------------ */ + +int saa7134_set_dmabits(struct saa7134_dev *dev) +{ + u32 split, task=0, ctrl=0, irq=0; + enum v4l2_field cap = V4L2_FIELD_ANY; + enum v4l2_field ov = V4L2_FIELD_ANY; + + assert_spin_locked(&dev->slock); + + if (dev->insuspend) + return 0; + + /* video capture -- dma 0 + video task A */ + if (dev->video_q.curr) { + task |= 0x01; + ctrl |= SAA7134_MAIN_CTRL_TE0; + irq |= SAA7134_IRQ1_INTE_RA0_1 | + SAA7134_IRQ1_INTE_RA0_0; + cap = dev->video_q.curr->vb.field; + } + + /* video capture -- dma 1+2 (planar modes) */ + if (dev->video_q.curr && + dev->video_q.curr->fmt->planar) { + ctrl |= SAA7134_MAIN_CTRL_TE4 | + SAA7134_MAIN_CTRL_TE5; + } + + /* screen overlay -- dma 0 + video task B */ + if (dev->ovenable) { + task |= 0x10; + ctrl |= SAA7134_MAIN_CTRL_TE1; + ov = dev->ovfield; + } + + /* vbi capture -- dma 0 + vbi task A+B */ + if (dev->vbi_q.curr) { + task |= 0x22; + ctrl |= SAA7134_MAIN_CTRL_TE2 | + SAA7134_MAIN_CTRL_TE3; + irq |= SAA7134_IRQ1_INTE_RA0_7 | + SAA7134_IRQ1_INTE_RA0_6 | + SAA7134_IRQ1_INTE_RA0_5 | + SAA7134_IRQ1_INTE_RA0_4; + } + + /* audio capture -- dma 3 */ + if (dev->dmasound.dma_running) { + ctrl |= SAA7134_MAIN_CTRL_TE6; + irq |= SAA7134_IRQ1_INTE_RA3_1 | + SAA7134_IRQ1_INTE_RA3_0; + } + + /* TS capture -- dma 5 */ + if (dev->ts_q.curr) { + ctrl |= SAA7134_MAIN_CTRL_TE5; + irq |= SAA7134_IRQ1_INTE_RA2_1 | + SAA7134_IRQ1_INTE_RA2_0; + } + + /* set task conditions + field handling */ + if (V4L2_FIELD_HAS_BOTH(cap) || V4L2_FIELD_HAS_BOTH(ov) || cap == ov) { + /* default config -- use full frames */ + saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d); + saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d); + saa_writeb(SAA7134_FIELD_HANDLING(TASK_A), 0x02); + saa_writeb(SAA7134_FIELD_HANDLING(TASK_B), 0x02); + split = 0; + } else { + /* split fields between tasks */ + if (V4L2_FIELD_TOP == cap) { + /* odd A, even B, repeat */ + saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0d); + saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0e); + } else { + /* odd B, even A, repeat */ + saa_writeb(SAA7134_TASK_CONDITIONS(TASK_A), 0x0e); + saa_writeb(SAA7134_TASK_CONDITIONS(TASK_B), 0x0d); + } + saa_writeb(SAA7134_FIELD_HANDLING(TASK_A), 0x01); + saa_writeb(SAA7134_FIELD_HANDLING(TASK_B), 0x01); + split = 1; + } + + /* irqs */ + saa_writeb(SAA7134_REGION_ENABLE, task); + saa_writel(SAA7134_IRQ1, irq); + saa_andorl(SAA7134_MAIN_CTRL, + SAA7134_MAIN_CTRL_TE0 | + SAA7134_MAIN_CTRL_TE1 | + SAA7134_MAIN_CTRL_TE2 | + SAA7134_MAIN_CTRL_TE3 | + SAA7134_MAIN_CTRL_TE4 | + SAA7134_MAIN_CTRL_TE5 | + SAA7134_MAIN_CTRL_TE6, + ctrl); + dprintk("dmabits: task=0x%02x ctrl=0x%02x irq=0x%x split=%s\n", + task, ctrl, irq, split ? "no" : "yes"); + + return 0; +} + +/* ------------------------------------------------------------------ */ +/* IRQ handler + helpers */ + +static char *irqbits[] = { + "DONE_RA0", "DONE_RA1", "DONE_RA2", "DONE_RA3", + "AR", "PE", "PWR_ON", "RDCAP", "INTL", "FIDT", "MMC", + "TRIG_ERR", "CONF_ERR", "LOAD_ERR", + "GPIO16", "GPIO18", "GPIO22", "GPIO23" +}; +#define IRQBITS ARRAY_SIZE(irqbits) + +static void print_irqstatus(struct saa7134_dev *dev, int loop, + unsigned long report, unsigned long status) +{ + unsigned int i; + + printk(KERN_DEBUG "%s/irq[%d,%ld]: r=0x%lx s=0x%02lx", + dev->name,loop,jiffies,report,status); + for (i = 0; i < IRQBITS; i++) { + if (!(report & (1 << i))) + continue; + printk(" %s",irqbits[i]); + } + if (report & SAA7134_IRQ_REPORT_DONE_RA0) { + printk(" | RA0=%s,%s,%s,%ld", + (status & 0x40) ? "vbi" : "video", + (status & 0x20) ? "b" : "a", + (status & 0x10) ? "odd" : "even", + (status & 0x0f)); + } + printk("\n"); +} + +static irqreturn_t saa7134_irq(int irq, void *dev_id) +{ + struct saa7134_dev *dev = (struct saa7134_dev*) dev_id; + unsigned long report,status; + int loop, handled = 0; + + if (dev->insuspend) + goto out; + + for (loop = 0; loop < 10; loop++) { + report = saa_readl(SAA7134_IRQ_REPORT); + status = saa_readl(SAA7134_IRQ_STATUS); + + /* If dmasound support is active and we get a sound report, + * mask out the report and let the saa7134-alsa module deal + * with it */ + if ((report & SAA7134_IRQ_REPORT_DONE_RA3) && + (dev->dmasound.priv_data != NULL) ) + { + if (irq_debug > 1) + printk(KERN_DEBUG "%s/irq: preserving DMA sound interrupt\n", + dev->name); + report &= ~SAA7134_IRQ_REPORT_DONE_RA3; + } + + if (0 == report) { + if (irq_debug > 1) + printk(KERN_DEBUG "%s/irq: no (more) work\n", + dev->name); + goto out; + } + + handled = 1; + saa_writel(SAA7134_IRQ_REPORT,report); + if (irq_debug) + print_irqstatus(dev,loop,report,status); + + + if ((report & SAA7134_IRQ_REPORT_RDCAP) || + (report & SAA7134_IRQ_REPORT_INTL)) + saa7134_irq_video_signalchange(dev); + + + if ((report & SAA7134_IRQ_REPORT_DONE_RA0) && + (status & 0x60) == 0) + saa7134_irq_video_done(dev,status); + + if ((report & SAA7134_IRQ_REPORT_DONE_RA0) && + (status & 0x40) == 0x40) + saa7134_irq_vbi_done(dev,status); + + if ((report & SAA7134_IRQ_REPORT_DONE_RA2) && + card_has_mpeg(dev)) + saa7134_irq_ts_done(dev,status); + + if (report & SAA7134_IRQ_REPORT_GPIO16) { + switch (dev->has_remote) { + case SAA7134_REMOTE_GPIO: + if (!dev->remote) + break; + if (dev->remote->mask_keydown & 0x10000) { + saa7134_input_irq(dev); + } + break; + + case SAA7134_REMOTE_I2C: + break; /* FIXME: invoke I2C get_key() */ + + default: /* GPIO16 not used by IR remote */ + break; + } + } + + if (report & SAA7134_IRQ_REPORT_GPIO18) { + switch (dev->has_remote) { + case SAA7134_REMOTE_GPIO: + if (!dev->remote) + break; + if ((dev->remote->mask_keydown & 0x40000) || + (dev->remote->mask_keyup & 0x40000)) { + saa7134_input_irq(dev); + } + break; + + case SAA7134_REMOTE_I2C: + break; /* FIXME: invoke I2C get_key() */ + + default: /* GPIO18 not used by IR remote */ + break; + } + } + } + + if (10 == loop) { + print_irqstatus(dev,loop,report,status); + if (report & SAA7134_IRQ_REPORT_PE) { + /* disable all parity error */ + printk(KERN_WARNING "%s/irq: looping -- " + "clearing PE (parity error!) enable bit\n",dev->name); + saa_clearl(SAA7134_IRQ2,SAA7134_IRQ2_INTE_PE); + } else if (report & SAA7134_IRQ_REPORT_GPIO16) { + /* disable gpio16 IRQ */ + printk(KERN_WARNING "%s/irq: looping -- " + "clearing GPIO16 enable bit\n",dev->name); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_P); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO16_N); + } else if (report & SAA7134_IRQ_REPORT_GPIO18) { + /* disable gpio18 IRQs */ + printk(KERN_WARNING "%s/irq: looping -- " + "clearing GPIO18 enable bit\n",dev->name); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_P); + saa_clearl(SAA7134_IRQ2, SAA7134_IRQ2_INTE_GPIO18_N); + } else { + /* disable all irqs */ + printk(KERN_WARNING "%s/irq: looping -- " + "clearing all enable bits\n",dev->name); + saa_writel(SAA7134_IRQ1,0); + saa_writel(SAA7134_IRQ2,0); + } + } + + out: + return IRQ_RETVAL(handled); +} + +/* ------------------------------------------------------------------ */ + +/* early init (no i2c, no irq) */ + +static int saa7134_hw_enable1(struct saa7134_dev *dev) +{ + /* RAM FIFO config */ + saa_writel(SAA7134_FIFO_SIZE, 0x08070503); + saa_writel(SAA7134_THRESHOULD, 0x02020202); + + /* enable audio + video processing */ + saa_writel(SAA7134_MAIN_CTRL, + SAA7134_MAIN_CTRL_VPLLE | + SAA7134_MAIN_CTRL_APLLE | + SAA7134_MAIN_CTRL_EXOSC | + SAA7134_MAIN_CTRL_EVFE1 | + SAA7134_MAIN_CTRL_EVFE2 | + SAA7134_MAIN_CTRL_ESFE | + SAA7134_MAIN_CTRL_EBDAC); + + /* + * Initialize OSS _after_ enabling audio clock PLL and audio processing. + * OSS initialization writes to registers via the audio DSP; these + * writes will fail unless the audio clock has been started. At worst, + * audio will not work. + */ + + /* enable peripheral devices */ + saa_writeb(SAA7134_SPECIAL_MODE, 0x01); + + /* set vertical line numbering start (vbi needs this) */ + saa_writeb(SAA7134_SOURCE_TIMING2, 0x20); + + return 0; +} + +static int saa7134_hwinit1(struct saa7134_dev *dev) +{ + dprintk("hwinit1\n"); + + saa_writel(SAA7134_IRQ1, 0); + saa_writel(SAA7134_IRQ2, 0); + + /* Clear any stale IRQ reports */ + saa_writel(SAA7134_IRQ_REPORT, saa_readl(SAA7134_IRQ_REPORT)); + + mutex_init(&dev->lock); + spin_lock_init(&dev->slock); + + saa7134_track_gpio(dev,"pre-init"); + saa7134_video_init1(dev); + saa7134_vbi_init1(dev); + if (card_has_mpeg(dev)) + saa7134_ts_init1(dev); + saa7134_input_init1(dev); + + saa7134_hw_enable1(dev); + + return 0; +} + +/* late init (with i2c + irq) */ +static int saa7134_hw_enable2(struct saa7134_dev *dev) +{ + + unsigned int irq2_mask; + + /* enable IRQ's */ + irq2_mask = + SAA7134_IRQ2_INTE_DEC3 | + SAA7134_IRQ2_INTE_DEC2 | + SAA7134_IRQ2_INTE_DEC1 | + SAA7134_IRQ2_INTE_DEC0 | + SAA7134_IRQ2_INTE_PE | + SAA7134_IRQ2_INTE_AR; + + if (dev->has_remote == SAA7134_REMOTE_GPIO && dev->remote) { + if (dev->remote->mask_keydown & 0x10000) + irq2_mask |= SAA7134_IRQ2_INTE_GPIO16_N; + else { /* Allow enabling both IRQ edge triggers */ + if (dev->remote->mask_keydown & 0x40000) + irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_P; + if (dev->remote->mask_keyup & 0x40000) + irq2_mask |= SAA7134_IRQ2_INTE_GPIO18_N; + } + } + + if (dev->has_remote == SAA7134_REMOTE_I2C) { + request_module("ir-kbd-i2c"); + } + + saa_writel(SAA7134_IRQ1, 0); + saa_writel(SAA7134_IRQ2, irq2_mask); + + return 0; +} + +static int saa7134_hwinit2(struct saa7134_dev *dev) +{ + + dprintk("hwinit2\n"); + + saa7134_video_init2(dev); + saa7134_tvaudio_init2(dev); + + saa7134_hw_enable2(dev); + + return 0; +} + + +/* shutdown */ +static int saa7134_hwfini(struct saa7134_dev *dev) +{ + dprintk("hwfini\n"); + + if (card_has_mpeg(dev)) + saa7134_ts_fini(dev); + saa7134_input_fini(dev); + saa7134_vbi_fini(dev); + saa7134_tvaudio_fini(dev); + return 0; +} + +static void __devinit must_configure_manually(int has_eeprom) +{ + unsigned int i,p; + + if (!has_eeprom) + printk(KERN_WARNING + "saa7134: <rant>\n" + "saa7134: Congratulations! Your TV card vendor saved a few\n" + "saa7134: cents for a eeprom, thus your pci board has no\n" + "saa7134: subsystem ID and I can't identify it automatically\n" + "saa7134: </rant>\n" + "saa7134: I feel better now. Ok, here are the good news:\n" + "saa7134: You can use the card=<nr> insmod option to specify\n" + "saa7134: which board do you have. The list:\n"); + else + printk(KERN_WARNING + "saa7134: Board is currently unknown. You might try to use the card=<nr>\n" + "saa7134: insmod option to specify which board do you have, but this is\n" + "saa7134: somewhat risky, as might damage your card. It is better to ask\n" + "saa7134: for support at linux-media@vger.kernel.org.\n" + "saa7134: The supported cards are:\n"); + + for (i = 0; i < saa7134_bcount; i++) { + printk(KERN_WARNING "saa7134: card=%d -> %-40.40s", + i,saa7134_boards[i].name); + for (p = 0; saa7134_pci_tbl[p].driver_data; p++) { + if (saa7134_pci_tbl[p].driver_data != i) + continue; + printk(" %04x:%04x", + saa7134_pci_tbl[p].subvendor, + saa7134_pci_tbl[p].subdevice); + } + printk("\n"); + } +} + +static struct video_device *vdev_init(struct saa7134_dev *dev, + struct video_device *template, + char *type) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + *vfd = *template; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release; + vfd->debug = video_debug; + snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", + dev->name, type, saa7134_boards[dev->board].name); + video_set_drvdata(vfd, dev); + return vfd; +} + +static void saa7134_unregister_video(struct saa7134_dev *dev) +{ + if (dev->video_dev) { + if (video_is_registered(dev->video_dev)) + video_unregister_device(dev->video_dev); + else + video_device_release(dev->video_dev); + dev->video_dev = NULL; + } + if (dev->vbi_dev) { + if (video_is_registered(dev->vbi_dev)) + video_unregister_device(dev->vbi_dev); + else + video_device_release(dev->vbi_dev); + dev->vbi_dev = NULL; + } + if (dev->radio_dev) { + if (video_is_registered(dev->radio_dev)) + video_unregister_device(dev->radio_dev); + else + video_device_release(dev->radio_dev); + dev->radio_dev = NULL; + } +} + +static void mpeg_ops_attach(struct saa7134_mpeg_ops *ops, + struct saa7134_dev *dev) +{ + int err; + + if (NULL != dev->mops) + return; + if (saa7134_boards[dev->board].mpeg != ops->type) + return; + err = ops->init(dev); + if (0 != err) + return; + dev->mops = ops; +} + +static void mpeg_ops_detach(struct saa7134_mpeg_ops *ops, + struct saa7134_dev *dev) +{ + if (NULL == dev->mops) + return; + if (dev->mops != ops) + return; + dev->mops->fini(dev); + dev->mops = NULL; +} + +static int __devinit saa7134_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct saa7134_dev *dev; + struct saa7134_mpeg_ops *mops; + int err; + + if (saa7134_devcount == SAA7134_MAXBOARDS) + return -ENOMEM; + + dev = kzalloc(sizeof(*dev),GFP_KERNEL); + if (NULL == dev) + return -ENOMEM; + + err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); + if (err) + goto fail0; + + /* pci init */ + dev->pci = pci_dev; + if (pci_enable_device(pci_dev)) { + err = -EIO; + goto fail1; + } + + dev->nr = saa7134_devcount; + sprintf(dev->name,"saa%x[%d]",pci_dev->device,dev->nr); + + /* pci quirks */ + if (pci_pci_problems) { + if (pci_pci_problems & PCIPCI_TRITON) + printk(KERN_INFO "%s: quirk: PCIPCI_TRITON\n", dev->name); + if (pci_pci_problems & PCIPCI_NATOMA) + printk(KERN_INFO "%s: quirk: PCIPCI_NATOMA\n", dev->name); + if (pci_pci_problems & PCIPCI_VIAETBF) + printk(KERN_INFO "%s: quirk: PCIPCI_VIAETBF\n", dev->name); + if (pci_pci_problems & PCIPCI_VSFX) + printk(KERN_INFO "%s: quirk: PCIPCI_VSFX\n",dev->name); +#ifdef PCIPCI_ALIMAGIK + if (pci_pci_problems & PCIPCI_ALIMAGIK) { + printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n", + dev->name); + latency = 0x0A; + } +#endif + if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) { + printk(KERN_INFO "%s: quirk: this driver and your " + "chipset may not work together" + " in overlay mode.\n",dev->name); + if (!saa7134_no_overlay) { + printk(KERN_INFO "%s: quirk: overlay " + "mode will be disabled.\n", + dev->name); + saa7134_no_overlay = 1; + } else { + printk(KERN_INFO "%s: quirk: overlay " + "mode will be forced. Use this" + " option at your own risk.\n", + dev->name); + } + } + } + if (UNSET != latency) { + printk(KERN_INFO "%s: setting pci latency timer to %d\n", + dev->name,latency); + pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency); + } + + /* print pci info */ + dev->pci_rev = pci_dev->revision; + pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); + printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, " + "latency: %d, mmio: 0x%llx\n", dev->name, + pci_name(pci_dev), dev->pci_rev, pci_dev->irq, + dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0)); + pci_set_master(pci_dev); + if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) { + printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name); + err = -EIO; + goto fail1; + } + + /* board config */ + dev->board = pci_id->driver_data; + if (card[dev->nr] >= 0 && + card[dev->nr] < saa7134_bcount) + dev->board = card[dev->nr]; + if (SAA7134_BOARD_UNKNOWN == dev->board) + must_configure_manually(0); + else if (SAA7134_BOARD_NOAUTO == dev->board) { + must_configure_manually(1); + dev->board = SAA7134_BOARD_UNKNOWN; + } + dev->autodetected = card[dev->nr] != dev->board; + dev->tuner_type = saa7134_boards[dev->board].tuner_type; + dev->tuner_addr = saa7134_boards[dev->board].tuner_addr; + dev->radio_type = saa7134_boards[dev->board].radio_type; + dev->radio_addr = saa7134_boards[dev->board].radio_addr; + dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf; + if (UNSET != tuner[dev->nr]) + dev->tuner_type = tuner[dev->nr]; + printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", + dev->name,pci_dev->subsystem_vendor, + pci_dev->subsystem_device,saa7134_boards[dev->board].name, + dev->board, dev->autodetected ? + "autodetected" : "insmod option"); + + /* get mmio */ + if (!request_mem_region(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0), + dev->name)) { + err = -EBUSY; + printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n", + dev->name,(unsigned long long)pci_resource_start(pci_dev,0)); + goto fail1; + } + dev->lmmio = ioremap(pci_resource_start(pci_dev, 0), + pci_resource_len(pci_dev, 0)); + dev->bmmio = (__u8 __iomem *)dev->lmmio; + if (NULL == dev->lmmio) { + err = -EIO; + printk(KERN_ERR "%s: can't ioremap() MMIO memory\n", + dev->name); + goto fail2; + } + + /* initialize hardware #1 */ + saa7134_board_init1(dev); + saa7134_hwinit1(dev); + + /* get irq */ + err = request_irq(pci_dev->irq, saa7134_irq, + IRQF_SHARED | IRQF_DISABLED, dev->name, dev); + if (err < 0) { + printk(KERN_ERR "%s: can't get IRQ %d\n", + dev->name,pci_dev->irq); + goto fail3; + } + + /* wait a bit, register i2c bus */ + msleep(100); + saa7134_i2c_register(dev); + saa7134_board_init2(dev); + + saa7134_hwinit2(dev); + + /* load i2c helpers */ + if (card_is_empress(dev)) { + struct v4l2_subdev *sd = + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "saa6752hs", + saa7134_boards[dev->board].empress_addr, NULL); + + if (sd) + sd->grp_id = GRP_EMPRESS; + } + + if (saa7134_boards[dev->board].rds_addr) { + struct v4l2_subdev *sd; + + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "saa6588", + 0, I2C_ADDRS(saa7134_boards[dev->board].rds_addr)); + if (sd) { + printk(KERN_INFO "%s: found RDS decoder\n", dev->name); + dev->has_rds = 1; + } + } + + v4l2_prio_init(&dev->prio); + + mutex_lock(&saa7134_devlist_lock); + list_for_each_entry(mops, &mops_list, next) + mpeg_ops_attach(mops, dev); + list_add_tail(&dev->devlist, &saa7134_devlist); + mutex_unlock(&saa7134_devlist_lock); + + /* check for signal */ + saa7134_irq_video_signalchange(dev); + + if (TUNER_ABSENT != dev->tuner_type) + saa_call_all(dev, core, s_power, 0); + + /* register v4l devices */ + if (saa7134_no_overlay > 0) + printk(KERN_INFO "%s: Overlay support disabled.\n", dev->name); + + dev->video_dev = vdev_init(dev,&saa7134_video_template,"video"); + err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, + video_nr[dev->nr]); + if (err < 0) { + printk(KERN_INFO "%s: can't register video device\n", + dev->name); + goto fail4; + } + printk(KERN_INFO "%s: registered device %s [v4l2]\n", + dev->name, video_device_node_name(dev->video_dev)); + + dev->vbi_dev = vdev_init(dev, &saa7134_video_template, "vbi"); + + err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI, + vbi_nr[dev->nr]); + if (err < 0) + goto fail4; + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(dev->vbi_dev)); + + if (card_has_radio(dev)) { + dev->radio_dev = vdev_init(dev,&saa7134_radio_template,"radio"); + err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO, + radio_nr[dev->nr]); + if (err < 0) + goto fail4; + printk(KERN_INFO "%s: registered device %s\n", + dev->name, video_device_node_name(dev->radio_dev)); + } + + /* everything worked */ + saa7134_devcount++; + + if (saa7134_dmasound_init && !dev->dmasound.priv_data) + saa7134_dmasound_init(dev); + + request_submodules(dev); + return 0; + + fail4: + saa7134_unregister_video(dev); + saa7134_i2c_unregister(dev); + free_irq(pci_dev->irq, dev); + fail3: + saa7134_hwfini(dev); + iounmap(dev->lmmio); + fail2: + release_mem_region(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0)); + fail1: + v4l2_device_unregister(&dev->v4l2_dev); + fail0: + kfree(dev); + return err; +} + +static void __devexit saa7134_finidev(struct pci_dev *pci_dev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev); + struct saa7134_mpeg_ops *mops; + + flush_request_submodules(dev); + + /* Release DMA sound modules if present */ + if (saa7134_dmasound_exit && dev->dmasound.priv_data) { + saa7134_dmasound_exit(dev); + } + + /* debugging ... */ + if (irq_debug) { + u32 report = saa_readl(SAA7134_IRQ_REPORT); + u32 status = saa_readl(SAA7134_IRQ_STATUS); + print_irqstatus(dev,42,report,status); + } + + /* disable peripheral devices */ + saa_writeb(SAA7134_SPECIAL_MODE,0); + + /* shutdown hardware */ + saa_writel(SAA7134_IRQ1,0); + saa_writel(SAA7134_IRQ2,0); + saa_writel(SAA7134_MAIN_CTRL,0); + + /* shutdown subsystems */ + saa7134_hwfini(dev); + + /* unregister */ + mutex_lock(&saa7134_devlist_lock); + list_del(&dev->devlist); + list_for_each_entry(mops, &mops_list, next) + mpeg_ops_detach(mops, dev); + mutex_unlock(&saa7134_devlist_lock); + saa7134_devcount--; + + saa7134_i2c_unregister(dev); + saa7134_unregister_video(dev); + + + /* the DMA sound modules should be unloaded before reaching + this, but just in case they are still present... */ + if (dev->dmasound.priv_data != NULL) { + free_irq(pci_dev->irq, &dev->dmasound); + dev->dmasound.priv_data = NULL; + } + + + /* release resources */ + free_irq(pci_dev->irq, dev); + iounmap(dev->lmmio); + release_mem_region(pci_resource_start(pci_dev,0), + pci_resource_len(pci_dev,0)); + + + v4l2_device_unregister(&dev->v4l2_dev); + + /* free memory */ + kfree(dev); +} + +#ifdef CONFIG_PM + +/* resends a current buffer in queue after resume */ +static int saa7134_buffer_requeue(struct saa7134_dev *dev, + struct saa7134_dmaqueue *q) +{ + struct saa7134_buf *buf, *next; + + assert_spin_locked(&dev->slock); + + buf = q->curr; + next = buf; + dprintk("buffer_requeue\n"); + + if (!buf) + return 0; + + dprintk("buffer_requeue : resending active buffers \n"); + + if (!list_empty(&q->queue)) + next = list_entry(q->queue.next, struct saa7134_buf, + vb.queue); + buf->activate(dev, buf, next); + + return 0; +} + +static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev); + + /* disable overlay - apps should enable it explicitly on resume*/ + dev->ovenable = 0; + + /* Disable interrupts, DMA, and rest of the chip*/ + saa_writel(SAA7134_IRQ1, 0); + saa_writel(SAA7134_IRQ2, 0); + saa_writel(SAA7134_MAIN_CTRL, 0); + + dev->insuspend = 1; + synchronize_irq(pci_dev->irq); + + /* ACK interrupts once more, just in case, + since the IRQ handler won't ack them anymore*/ + + saa_writel(SAA7134_IRQ_REPORT, saa_readl(SAA7134_IRQ_REPORT)); + + /* Disable timeout timers - if we have active buffers, we will + fill them on resume*/ + + del_timer(&dev->video_q.timeout); + del_timer(&dev->vbi_q.timeout); + del_timer(&dev->ts_q.timeout); + + if (dev->remote) + saa7134_ir_stop(dev); + + pci_save_state(pci_dev); + pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state)); + + return 0; +} + +static int saa7134_resume(struct pci_dev *pci_dev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev); + unsigned long flags; + + pci_set_power_state(pci_dev, PCI_D0); + pci_restore_state(pci_dev); + + /* Do things that are done in saa7134_initdev , + except of initializing memory structures.*/ + + saa7134_board_init1(dev); + + /* saa7134_hwinit1 */ + if (saa7134_boards[dev->board].video_out) + saa7134_videoport_init(dev); + if (card_has_mpeg(dev)) + saa7134_ts_init_hw(dev); + if (dev->remote) + saa7134_ir_start(dev); + saa7134_hw_enable1(dev); + + msleep(100); + + saa7134_board_init2(dev); + + /*saa7134_hwinit2*/ + saa7134_set_tvnorm_hw(dev); + saa7134_tvaudio_setmute(dev); + saa7134_tvaudio_setvolume(dev, dev->ctl_volume); + saa7134_tvaudio_init(dev); + saa7134_enable_i2s(dev); + saa7134_hw_enable2(dev); + + saa7134_irq_video_signalchange(dev); + + /*resume unfinished buffer(s)*/ + spin_lock_irqsave(&dev->slock, flags); + saa7134_buffer_requeue(dev, &dev->video_q); + saa7134_buffer_requeue(dev, &dev->vbi_q); + saa7134_buffer_requeue(dev, &dev->ts_q); + + /* FIXME: Disable DMA audio sound - temporary till proper support + is implemented*/ + + dev->dmasound.dma_running = 0; + + /* start DMA now*/ + dev->insuspend = 0; + smp_wmb(); + saa7134_set_dmabits(dev); + spin_unlock_irqrestore(&dev->slock, flags); + + return 0; +} +#endif + +/* ----------------------------------------------------------- */ + +int saa7134_ts_register(struct saa7134_mpeg_ops *ops) +{ + struct saa7134_dev *dev; + + mutex_lock(&saa7134_devlist_lock); + list_for_each_entry(dev, &saa7134_devlist, devlist) + mpeg_ops_attach(ops, dev); + list_add_tail(&ops->next,&mops_list); + mutex_unlock(&saa7134_devlist_lock); + return 0; +} + +void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops) +{ + struct saa7134_dev *dev; + + mutex_lock(&saa7134_devlist_lock); + list_del(&ops->next); + list_for_each_entry(dev, &saa7134_devlist, devlist) + mpeg_ops_detach(ops, dev); + mutex_unlock(&saa7134_devlist_lock); +} + +EXPORT_SYMBOL(saa7134_ts_register); +EXPORT_SYMBOL(saa7134_ts_unregister); + +/* ----------------------------------------------------------- */ + +static struct pci_driver saa7134_pci_driver = { + .name = "saa7134", + .id_table = saa7134_pci_tbl, + .probe = saa7134_initdev, + .remove = __devexit_p(saa7134_finidev), +#ifdef CONFIG_PM + .suspend = saa7134_suspend, + .resume = saa7134_resume +#endif +}; + +static int __init saa7134_init(void) +{ + INIT_LIST_HEAD(&saa7134_devlist); + printk(KERN_INFO "saa7130/34: v4l2 driver version %s loaded\n", + SAA7134_VERSION); + return pci_register_driver(&saa7134_pci_driver); +} + +static void __exit saa7134_fini(void) +{ + pci_unregister_driver(&saa7134_pci_driver); +} + +module_init(saa7134_init); +module_exit(saa7134_fini); + +/* ----------------------------------------------------------- */ + +EXPORT_SYMBOL(saa7134_set_gpio); +EXPORT_SYMBOL(saa7134_boards); + +/* ----------------- for the DMA sound modules --------------- */ + +EXPORT_SYMBOL(saa7134_dmasound_init); +EXPORT_SYMBOL(saa7134_dmasound_exit); +EXPORT_SYMBOL(saa7134_pgtable_free); +EXPORT_SYMBOL(saa7134_pgtable_build); +EXPORT_SYMBOL(saa7134_pgtable_alloc); +EXPORT_SYMBOL(saa7134_set_dmabits); + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/pci/saa7134/saa7134-dvb.c b/drivers/media/pci/saa7134/saa7134-dvb.c new file mode 100644 index 000000000000..b209de40a4f8 --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-dvb.c @@ -0,0 +1,1936 @@ +/* + * + * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] + * + * Extended 3 / 2005 by Hartmut Hackmann to support various + * cards with the tda10046 DVB-T channel decoder + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/suspend.h> + +#include "saa7134-reg.h" +#include "saa7134.h" +#include <media/v4l2-common.h> +#include "dvb-pll.h" +#include <dvb_frontend.h> + +#include "mt352.h" +#include "mt352_priv.h" /* FIXME */ +#include "tda1004x.h" +#include "nxt200x.h" +#include "tuner-xc2028.h" +#include "xc5000.h" + +#include "tda10086.h" +#include "tda826x.h" +#include "tda827x.h" +#include "isl6421.h" +#include "isl6405.h" +#include "lnbp21.h" +#include "tuner-simple.h" +#include "tda10048.h" +#include "tda18271.h" +#include "lgdt3305.h" +#include "tda8290.h" +#include "mb86a20s.h" +#include "lgs8gxx.h" + +#include "zl10353.h" +#include "qt1010.h" + +#include "zl10036.h" +#include "zl10039.h" +#include "mt312.h" +#include "s5h1411.h" + +MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); +MODULE_LICENSE("GPL"); + +static unsigned int antenna_pwr; + +module_param(antenna_pwr, int, 0444); +MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)"); + +static int use_frontend; +module_param(use_frontend, int, 0644); +MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off)."); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define dprintk(fmt, arg...) do { if (debug) \ + printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg); } while(0) + +/* Print a warning */ +#define wprintk(fmt, arg...) \ + printk(KERN_WARNING "%s/dvb: " fmt, dev->name, ## arg) + +/* ------------------------------------------------------------------ + * mt352 based DVB-T cards + */ + +static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on) +{ + u32 ok; + + if (!on) { + saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 26)); + saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26)); + return 0; + } + + saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 26)); + saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26)); + udelay(10); + + saa_setl(SAA7134_GPIO_GPMODE0 >> 2, (1 << 28)); + saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28)); + udelay(10); + saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28)); + udelay(10); + ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27); + dprintk("%s %s\n", __func__, ok ? "on" : "off"); + + if (!ok) + saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26)); + return ok; +} + +static int mt352_pinnacle_init(struct dvb_frontend* fe) +{ + static u8 clock_config [] = { CLOCK_CTL, 0x3d, 0x28 }; + static u8 reset [] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x31 }; + static u8 fsm_ctl_cfg[] = { 0x7b, 0x04 }; + static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x0f }; + static u8 scan_ctl_cfg [] = { SCAN_CTL, 0x0d }; + static u8 irq_cfg [] = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 }; + struct saa7134_dev *dev= fe->dvb->priv; + + dprintk("%s called\n", __func__); + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg)); + + mt352_write(fe, fsm_ctl_cfg, sizeof(fsm_ctl_cfg)); + mt352_write(fe, scan_ctl_cfg, sizeof(scan_ctl_cfg)); + mt352_write(fe, irq_cfg, sizeof(irq_cfg)); + + return 0; +} + +static int mt352_aver777_init(struct dvb_frontend* fe) +{ + static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; + static u8 reset [] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0xa0 }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 }; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + + return 0; +} + +static int mt352_avermedia_xc3028_init(struct dvb_frontend *fe) +{ + static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x2d }; + static u8 reset [] = { RESET, 0x80 }; + static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 }; + static u8 agc_cfg [] = { AGC_TARGET, 0xe }; + static u8 capt_range_cfg[] = { CAPT_RANGE, 0x33 }; + + mt352_write(fe, clock_config, sizeof(clock_config)); + udelay(200); + mt352_write(fe, reset, sizeof(reset)); + mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg)); + mt352_write(fe, agc_cfg, sizeof(agc_cfg)); + mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg)); + return 0; +} + +static int mt352_pinnacle_tuner_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 off[] = { 0x00, 0xf1}; + u8 on[] = { 0x00, 0x71}; + struct i2c_msg msg = {.addr=0x43, .flags=0, .buf=off, .len = sizeof(off)}; + + struct saa7134_dev *dev = fe->dvb->priv; + struct v4l2_frequency f; + + /* set frequency (mt2050) */ + f.tuner = 0; + f.type = V4L2_TUNER_DIGITAL_TV; + f.frequency = c->frequency / 1000 * 16 / 1000; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&dev->i2c_adap, &msg, 1); + saa_call_all(dev, tuner, s_frequency, &f); + msg.buf = on; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&dev->i2c_adap, &msg, 1); + + pinnacle_antenna_pwr(dev, antenna_pwr); + + /* mt352 setup */ + return mt352_pinnacle_init(fe); +} + +static struct mt352_config pinnacle_300i = { + .demod_address = 0x3c >> 1, + .adc_clock = 20333, + .if2 = 36150, + .no_tuner = 1, + .demod_init = mt352_pinnacle_init, +}; + +static struct mt352_config avermedia_777 = { + .demod_address = 0xf, + .demod_init = mt352_aver777_init, +}; + +static struct mt352_config avermedia_xc3028_mt352_dev = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .demod_init = mt352_avermedia_xc3028_init, +}; + +static struct tda18271_std_map mb86a20s_tda18271_std_map = { + .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, + .if_lvl = 7, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config kworld_tda18271_config = { + .std_map = &mb86a20s_tda18271_std_map, + .gate = TDA18271_GATE_DIGITAL, + .config = 3, /* Use tuner callback for AGC */ + +}; + +static const struct mb86a20s_config kworld_mb86a20s_config = { + .demod_address = 0x10, +}; + +static int kworld_sbtvd_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct saa7134_dev *dev = fe->dvb->priv; + + unsigned char initmsg[] = {0x45, 0x97}; + unsigned char msg_enable[] = {0x45, 0xc1}; + unsigned char msg_disable[] = {0x45, 0x81}; + struct i2c_msg msg = {.addr = 0x4b, .flags = 0, .buf = initmsg, .len = 2}; + + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { + wprintk("could not access the I2C gate\n"); + return -EIO; + } + if (enable) + msg.buf = msg_enable; + else + msg.buf = msg_disable; + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { + wprintk("could not access the I2C gate\n"); + return -EIO; + } + msleep(20); + return 0; +} + +/* ================================================================== + * tda1004x based DVB-T cards, helper functions + */ + +static int philips_tda1004x_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct saa7134_dev *dev = fe->dvb->priv; + return request_firmware(fw, name, &dev->pci->dev); +} + +/* ------------------------------------------------------------------ + * these tuners are tu1216, td1316(a) + */ + +static int philips_tda6651_pll_set(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; + u8 tuner_buf[4]; + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len = + sizeof(tuner_buf) }; + int tuner_frequency = 0; + u8 band, cp, filter; + + /* determine charge pump */ + tuner_frequency = c->frequency + 36166000; + if (tuner_frequency < 87000000) + return -EINVAL; + else if (tuner_frequency < 130000000) + cp = 3; + else if (tuner_frequency < 160000000) + cp = 5; + else if (tuner_frequency < 200000000) + cp = 6; + else if (tuner_frequency < 290000000) + cp = 3; + else if (tuner_frequency < 420000000) + cp = 5; + else if (tuner_frequency < 480000000) + cp = 6; + else if (tuner_frequency < 620000000) + cp = 3; + else if (tuner_frequency < 830000000) + cp = 5; + else if (tuner_frequency < 895000000) + cp = 7; + else + return -EINVAL; + + /* determine band */ + if (c->frequency < 49000000) + return -EINVAL; + else if (c->frequency < 161000000) + band = 1; + else if (c->frequency < 444000000) + band = 2; + else if (c->frequency < 861000000) + band = 4; + else + return -EINVAL; + + /* setup PLL filter */ + switch (c->bandwidth_hz) { + case 6000000: + filter = 0; + break; + + case 7000000: + filter = 0; + break; + + case 8000000: + filter = 1; + break; + + default: + return -EINVAL; + } + + /* calculate divisor + * ((36166000+((1000000/6)/2)) + Finput)/(1000000/6) + */ + tuner_frequency = (((c->frequency / 1000) * 6) + 217496) / 1000; + + /* setup tuner buffer */ + tuner_buf[0] = (tuner_frequency >> 8) & 0x7f; + tuner_buf[1] = tuner_frequency & 0xff; + tuner_buf[2] = 0xca; + tuner_buf[3] = (cp << 5) | (filter << 3) | band; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) { + wprintk("could not write to tuner at addr: 0x%02x\n", + addr << 1); + return -EIO; + } + msleep(1); + return 0; +} + +static int philips_tu1216_init(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; + static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; + + /* setup PLL configuration */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) + return -EIO; + msleep(1); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +static struct tda1004x_config philips_tu1216_60_config = { + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config philips_tu1216_61_config = { + + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware +}; + +/* ------------------------------------------------------------------ */ + +static int philips_td1316_tuner_init(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; + static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab }; + struct i2c_msg init_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) }; + + /* setup PLL configuration */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) + return -EIO; + return 0; +} + +static int philips_td1316_tuner_set_params(struct dvb_frontend *fe) +{ + return philips_tda6651_pll_set(fe); +} + +static int philips_td1316_tuner_sleep(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; + static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 }; + struct i2c_msg analog_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) }; + + /* switch the tuner to analog mode */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1) + return -EIO; + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int philips_europa_tuner_init(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + static u8 msg[] = { 0x00, 0x40}; + struct i2c_msg init_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) }; + + + if (philips_td1316_tuner_init(fe)) + return -EIO; + msleep(1); + if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) + return -EIO; + + return 0; +} + +static int philips_europa_tuner_sleep(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + + static u8 msg[] = { 0x00, 0x14 }; + struct i2c_msg analog_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) }; + + if (philips_td1316_tuner_sleep(fe)) + return -EIO; + + /* switch the board to analog mode */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&dev->i2c_adap, &analog_msg, 1); + return 0; +} + +static int philips_europa_demod_sleep(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + + if (dev->original_demod_sleep) + dev->original_demod_sleep(fe); + fe->ops.i2c_gate_ctrl(fe, 1); + return 0; +} + +static struct tda1004x_config philips_europa_config = { + + .demod_address = 0x8, + .invert = 0, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_IFO_AUTO_POS, + .if_freq = TDA10046_FREQ_052, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config medion_cardbus = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_IFO_AUTO_NEG, + .if_freq = TDA10046_FREQ_3613, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config technotrend_budget_t3000_config = { + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .tuner_address = 0x63, + .request_firmware = philips_tda1004x_request_firmware +}; + +/* ------------------------------------------------------------------ + * tda 1004x based cards with philips silicon tuner + */ + +static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable) +{ + struct tda1004x_state *state = fe->demodulator_priv; + + u8 addr = state->config->i2c_gate; + static u8 tda8290_close[] = { 0x21, 0xc0}; + static u8 tda8290_open[] = { 0x21, 0x80}; + struct i2c_msg tda8290_msg = {.addr = addr,.flags = 0, .len = 2}; + if (enable) { + tda8290_msg.buf = tda8290_close; + } else { + tda8290_msg.buf = tda8290_open; + } + if (i2c_transfer(state->i2c, &tda8290_msg, 1) != 1) { + struct saa7134_dev *dev = fe->dvb->priv; + wprintk("could not access tda8290 I2C gate\n"); + return -EIO; + } + msleep(20); + return 0; +} + +static int philips_tda827x_tuner_init(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + + switch (state->config->antenna_switch) { + case 0: break; + case 1: dprintk("setting GPIO21 to 0 (TV antenna?)\n"); + saa7134_set_gpio(dev, 21, 0); + break; + case 2: dprintk("setting GPIO21 to 1 (Radio antenna?)\n"); + saa7134_set_gpio(dev, 21, 1); + break; + } + return 0; +} + +static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + + switch (state->config->antenna_switch) { + case 0: break; + case 1: dprintk("setting GPIO21 to 1 (Radio antenna?)\n"); + saa7134_set_gpio(dev, 21, 1); + break; + case 2: dprintk("setting GPIO21 to 0 (TV antenna?)\n"); + saa7134_set_gpio(dev, 21, 0); + break; + } + return 0; +} + +static int configure_tda827x_fe(struct saa7134_dev *dev, + struct tda1004x_config *cdec_conf, + struct tda827x_config *tuner_conf) +{ + struct videobuf_dvb_frontend *fe0; + + /* Get the first frontend */ + fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); + + fe0->dvb.frontend = dvb_attach(tda10046_attach, cdec_conf, &dev->i2c_adap); + if (fe0->dvb.frontend) { + if (cdec_conf->i2c_gate) + fe0->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; + if (dvb_attach(tda827x_attach, fe0->dvb.frontend, + cdec_conf->tuner_address, + &dev->i2c_adap, tuner_conf)) + return 0; + + wprintk("no tda827x tuner found at addr: %02x\n", + cdec_conf->tuner_address); + } + return -EINVAL; +} + +/* ------------------------------------------------------------------ */ + +static struct tda827x_config tda827x_cfg_0 = { + .init = philips_tda827x_tuner_init, + .sleep = philips_tda827x_tuner_sleep, + .config = 0, + .switch_addr = 0 +}; + +static struct tda827x_config tda827x_cfg_1 = { + .init = philips_tda827x_tuner_init, + .sleep = philips_tda827x_tuner_sleep, + .config = 1, + .switch_addr = 0x4b +}; + +static struct tda827x_config tda827x_cfg_2 = { + .init = philips_tda827x_tuner_init, + .sleep = philips_tda827x_tuner_sleep, + .config = 2, + .switch_addr = 0x4b +}; + +static struct tda827x_config tda827x_cfg_2_sw42 = { + .init = philips_tda827x_tuner_init, + .sleep = philips_tda827x_tuner_sleep, + .config = 2, + .switch_addr = 0x42 +}; + +/* ------------------------------------------------------------------ */ + +static struct tda1004x_config tda827x_lifeview_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config philips_tiger_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .antenna_switch= 1, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config cinergy_ht_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config cinergy_ht_pci_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config philips_tiger_s_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .antenna_switch= 1, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config pinnacle_pctv_310i_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config hauppauge_hvr_1110_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config asus_p7131_dual_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .antenna_switch= 2, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config lifeview_trio_config = { + .demod_address = 0x09, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP00_I, + .if_freq = TDA10046_FREQ_045, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config tevion_dvbt220rf_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config md8800_dvbt_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config asus_p7131_4871_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .antenna_switch= 2, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config asus_p7131_hybrid_lna_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .antenna_switch= 2, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config kworld_dvb_t_210_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .antenna_switch= 1, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config avermedia_super_007_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x60, + .antenna_switch= 1, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config twinhan_dtv_dvb_3056_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x42, + .tuner_address = 0x61, + .antenna_switch = 1, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config asus_tiger_3in1_config = { + .demod_address = 0x0b, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .antenna_switch = 1, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct tda1004x_config asus_ps3_100_config = { + .demod_address = 0x0b, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .antenna_switch = 1, + .request_firmware = philips_tda1004x_request_firmware +}; + +/* ------------------------------------------------------------------ + * special case: this card uses saa713x GPIO22 for the mode switch + */ + +static int ads_duo_tuner_init(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + philips_tda827x_tuner_init(fe); + /* route TDA8275a AGC input to the channel decoder */ + saa7134_set_gpio(dev, 22, 1); + return 0; +} + +static int ads_duo_tuner_sleep(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + /* route TDA8275a AGC input to the analog IF chip*/ + saa7134_set_gpio(dev, 22, 0); + philips_tda827x_tuner_sleep(fe); + return 0; +} + +static struct tda827x_config ads_duo_cfg = { + .init = ads_duo_tuner_init, + .sleep = ads_duo_tuner_sleep, + .config = 0 +}; + +static struct tda1004x_config ads_tech_duo_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP00_I, + .if_freq = TDA10046_FREQ_045, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware +}; + +static struct zl10353_config behold_h6_config = { + .demod_address = 0x1e>>1, + .no_tuner = 1, + .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, +}; + +static struct xc5000_config behold_x7_tunerconfig = { + .i2c_address = 0xc2>>1, + .if_khz = 4560, + .radio_input = XC5000_RADIO_FM1, +}; + +static struct zl10353_config behold_x7_config = { + .demod_address = 0x1e>>1, + .if2 = 45600, + .no_tuner = 1, + .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, +}; + +static struct zl10353_config videomate_t750_zl10353_config = { + .demod_address = 0x0f, + .no_tuner = 1, + .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, +}; + +static struct qt1010_config videomate_t750_qt1010_config = { + .i2c_address = 0x62 +}; + + +/* ================================================================== + * tda10086 based DVB-S cards, helper functions + */ + +static struct tda10086_config flydvbs = { + .demod_address = 0x0e, + .invert = 0, + .diseqc_tone = 0, + .xtal_freq = TDA10086_XTAL_16M, +}; + +static struct tda10086_config sd1878_4m = { + .demod_address = 0x0e, + .invert = 0, + .diseqc_tone = 0, + .xtal_freq = TDA10086_XTAL_4M, +}; + +/* ------------------------------------------------------------------ + * special case: lnb supply is connected to the gated i2c + */ + +static int md8800_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + int res = -EIO; + struct saa7134_dev *dev = fe->dvb->priv; + if (fe->ops.i2c_gate_ctrl) { + fe->ops.i2c_gate_ctrl(fe, 1); + if (dev->original_set_voltage) + res = dev->original_set_voltage(fe, voltage); + fe->ops.i2c_gate_ctrl(fe, 0); + } + return res; +}; + +static int md8800_set_high_voltage(struct dvb_frontend *fe, long arg) +{ + int res = -EIO; + struct saa7134_dev *dev = fe->dvb->priv; + if (fe->ops.i2c_gate_ctrl) { + fe->ops.i2c_gate_ctrl(fe, 1); + if (dev->original_set_high_voltage) + res = dev->original_set_high_voltage(fe, arg); + fe->ops.i2c_gate_ctrl(fe, 0); + } + return res; +}; + +static int md8800_set_voltage2(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct saa7134_dev *dev = fe->dvb->priv; + u8 wbuf[2] = { 0x1f, 00 }; + u8 rbuf; + struct i2c_msg msg[] = { { .addr = 0x08, .flags = 0, .buf = wbuf, .len = 1 }, + { .addr = 0x08, .flags = I2C_M_RD, .buf = &rbuf, .len = 1 } }; + + if (i2c_transfer(&dev->i2c_adap, msg, 2) != 2) + return -EIO; + /* NOTE: this assumes that gpo1 is used, it might be bit 5 (gpo2) */ + if (voltage == SEC_VOLTAGE_18) + wbuf[1] = rbuf | 0x10; + else + wbuf[1] = rbuf & 0xef; + msg[0].len = 2; + i2c_transfer(&dev->i2c_adap, msg, 1); + return 0; +} + +static int md8800_set_high_voltage2(struct dvb_frontend *fe, long arg) +{ + struct saa7134_dev *dev = fe->dvb->priv; + wprintk("%s: sorry can't set high LNB supply voltage from here\n", __func__); + return -EIO; +} + +/* ================================================================== + * nxt200x based ATSC cards, helper functions + */ + +static struct nxt200x_config avertvhda180 = { + .demod_address = 0x0a, +}; + +static struct nxt200x_config kworldatsc110 = { + .demod_address = 0x0a, +}; + +/* ------------------------------------------------------------------ */ + +static struct mt312_config avertv_a700_mt312 = { + .demod_address = 0x0e, + .voltage_inverted = 1, +}; + +static struct zl10036_config avertv_a700_tuner = { + .tuner_address = 0x60, +}; + +static struct mt312_config zl10313_compro_s350_config = { + .demod_address = 0x0e, +}; + +static struct lgdt3305_config hcw_lgdt3305_config = { + .i2c_addr = 0x0e, + .mpeg_mode = LGDT3305_MPEG_SERIAL, + .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, + .deny_i2c_rptr = 1, + .spectral_inversion = 1, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, +}; + +static struct tda10048_config hcw_tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_SERIAL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3500, + .dtv8_if_freq_khz = TDA10048_IF_4000, + .clk_freq_khz = TDA10048_CLK_16000, + .disable_gate_access = 1, +}; + +static struct tda18271_std_map hauppauge_tda18271_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x58, }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x58, }, +}; + +static struct tda18271_config hcw_tda18271_config = { + .std_map = &hauppauge_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, + .config = 3, + .output_opt = TDA18271_OUTPUT_LT_OFF, +}; + +static struct tda829x_config tda829x_no_probe = { + .probe_tuner = TDA829X_DONT_PROBE, +}; + +static struct tda10048_config zolid_tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_PARALLEL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3500, + .dtv8_if_freq_khz = TDA10048_IF_4000, + .clk_freq_khz = TDA10048_CLK_16000, + .disable_gate_access = 1, +}; + +static struct tda18271_config zolid_tda18271_config = { + .gate = TDA18271_GATE_ANALOG, +}; + +static struct tda10048_config dtv1000s_tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_PARALLEL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3800, + .dtv8_if_freq_khz = TDA10048_IF_4300, + .clk_freq_khz = TDA10048_CLK_16000, + .disable_gate_access = 1, +}; + +static struct tda18271_std_map dtv1000s_tda18271_std_map = { + .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config dtv1000s_tda18271_config = { + .std_map = &dtv1000s_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, +}; + +static struct lgs8gxx_config prohdtv_pro2_lgs8g75_config = { + .prod = LGS8GXX_PROD_LGS8G75, + .demod_address = 0x1d, + .serial_ts = 0, + .ts_clk_pol = 1, + .ts_clk_gated = 0, + .if_clk_freq = 30400, /* 30.4 MHz */ + .if_freq = 4000, /* 4.00 MHz */ + .if_neg_center = 0, + .ext_adc = 0, + .adc_signed = 1, + .adc_vpp = 3, /* 2.0 Vpp */ + .if_neg_edge = 1, +}; + +static struct tda18271_config prohdtv_pro2_tda18271_config = { + .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, +}; + +static struct tda18271_std_map kworld_tda18271_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3, + .if_lvl = 6, .rfagc_top = 0x37 }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0, + .if_lvl = 6, .rfagc_top = 0x37 }, +}; + +static struct tda18271_config kworld_pc150u_tda18271_config = { + .std_map = &kworld_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, + .config = 3, /* Use tuner callback for AGC */ + .rf_cal_on_startup = 1 +}; + +static struct s5h1411_config kworld_s5h1411_config = { + .output_mode = S5H1411_PARALLEL_OUTPUT, + .gpio = S5H1411_GPIO_OFF, + .qam_if = S5H1411_IF_4000, + .vsb_if = S5H1411_IF_3250, + .inversion = S5H1411_INVERSION_ON, + .status_mode = S5H1411_DEMODLOCKING, + .mpeg_timing = + S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, +}; + + +/* ================================================================== + * Core code + */ + +static int dvb_init(struct saa7134_dev *dev) +{ + int ret; + int attach_xc3028 = 0; + struct videobuf_dvb_frontend *fe0; + + /* FIXME: add support for multi-frontend */ + mutex_init(&dev->frontends.lock); + INIT_LIST_HEAD(&dev->frontends.felist); + + printk(KERN_INFO "%s() allocating 1 frontend\n", __func__); + fe0 = videobuf_dvb_alloc_frontend(&dev->frontends, 1); + if (!fe0) { + printk(KERN_ERR "%s() failed to alloc\n", __func__); + return -ENOMEM; + } + + /* init struct videobuf_dvb */ + dev->ts.nr_bufs = 32; + dev->ts.nr_packets = 32*4; + fe0->dvb.name = dev->name; + videobuf_queue_sg_init(&fe0->dvb.dvbq, &saa7134_ts_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_ALTERNATE, + sizeof(struct saa7134_buf), + dev, NULL); + + switch (dev->board) { + case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL: + dprintk("pinnacle 300i dvb setup\n"); + fe0->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + fe0->dvb.frontend->ops.tuner_ops.set_params = mt352_pinnacle_tuner_set_params; + } + break; + case SAA7134_BOARD_AVERMEDIA_777: + case SAA7134_BOARD_AVERMEDIA_A16AR: + dprintk("avertv 777 dvb setup\n"); + fe0->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(simple_tuner_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x61, + TUNER_PHILIPS_TD1316); + } + break; + case SAA7134_BOARD_AVERMEDIA_A16D: + dprintk("AverMedia A16D dvb setup\n"); + fe0->dvb.frontend = dvb_attach(mt352_attach, + &avermedia_xc3028_mt352_dev, + &dev->i2c_adap); + attach_xc3028 = 1; + break; + case SAA7134_BOARD_MD7134: + fe0->dvb.frontend = dvb_attach(tda10046_attach, + &medion_cardbus, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(simple_tuner_attach, fe0->dvb.frontend, + &dev->i2c_adap, medion_cardbus.tuner_address, + TUNER_PHILIPS_FMD1216ME_MK3); + } + break; + case SAA7134_BOARD_PHILIPS_TOUGH: + fe0->dvb.frontend = dvb_attach(tda10046_attach, + &philips_tu1216_60_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + fe0->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init; + fe0->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set; + } + break; + case SAA7134_BOARD_FLYDVBTDUO: + case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: + if (configure_tda827x_fe(dev, &tda827x_lifeview_config, + &tda827x_cfg_0) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_PHILIPS_EUROPA: + case SAA7134_BOARD_VIDEOMATE_DVBT_300: + case SAA7134_BOARD_ASUS_EUROPA_HYBRID: + fe0->dvb.frontend = dvb_attach(tda10046_attach, + &philips_europa_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep; + fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep; + fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init; + fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep; + fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; + } + break; + case SAA7134_BOARD_TECHNOTREND_BUDGET_T3000: + fe0->dvb.frontend = dvb_attach(tda10046_attach, + &technotrend_budget_t3000_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep; + fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep; + fe0->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init; + fe0->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep; + fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; + } + break; + case SAA7134_BOARD_VIDEOMATE_DVBT_200: + fe0->dvb.frontend = dvb_attach(tda10046_attach, + &philips_tu1216_61_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + fe0->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init; + fe0->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set; + } + break; + case SAA7134_BOARD_KWORLD_DVBT_210: + if (configure_tda827x_fe(dev, &kworld_dvb_t_210_config, + &tda827x_cfg_2) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + fe0->dvb.frontend = dvb_attach(tda10048_attach, + &hcw_tda10048_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &hcw_tda18271_config); + } + break; + case SAA7134_BOARD_PHILIPS_TIGER: + if (configure_tda827x_fe(dev, &philips_tiger_config, + &tda827x_cfg_0) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_PINNACLE_PCTV_310i: + if (configure_tda827x_fe(dev, &pinnacle_pctv_310i_config, + &tda827x_cfg_1) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_HAUPPAUGE_HVR1110: + if (configure_tda827x_fe(dev, &hauppauge_hvr_1110_config, + &tda827x_cfg_1) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_HAUPPAUGE_HVR1150: + fe0->dvb.frontend = dvb_attach(lgdt3305_attach, + &hcw_lgdt3305_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &hcw_tda18271_config); + } + break; + case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + if (configure_tda827x_fe(dev, &asus_p7131_dual_config, + &tda827x_cfg_0) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_FLYDVBT_LR301: + if (configure_tda827x_fe(dev, &tda827x_lifeview_config, + &tda827x_cfg_0) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_FLYDVB_TRIO: + if (!use_frontend) { /* terrestrial */ + if (configure_tda827x_fe(dev, &lifeview_trio_config, + &tda827x_cfg_0) < 0) + goto detach_frontend; + } else { /* satellite */ + fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); + if (fe0->dvb.frontend) { + if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x63, + &dev->i2c_adap, 0) == NULL) { + wprintk("%s: Lifeview Trio, No tda826x found!\n", __func__); + goto detach_frontend; + } + if (dvb_attach(isl6421_attach, fe0->dvb.frontend, &dev->i2c_adap, + 0x08, 0, 0) == NULL) { + wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __func__); + goto detach_frontend; + } + } + } + break; + case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: + case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: + fe0->dvb.frontend = dvb_attach(tda10046_attach, + &ads_tech_duo_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + if (dvb_attach(tda827x_attach,fe0->dvb.frontend, + ads_tech_duo_config.tuner_address, &dev->i2c_adap, + &ads_duo_cfg) == NULL) { + wprintk("no tda827x tuner found at addr: %02x\n", + ads_tech_duo_config.tuner_address); + goto detach_frontend; + } + } else + wprintk("failed to attach tda10046\n"); + break; + case SAA7134_BOARD_TEVION_DVBT_220RF: + if (configure_tda827x_fe(dev, &tevion_dvbt220rf_config, + &tda827x_cfg_0) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_MEDION_MD8800_QUADRO: + if (!use_frontend) { /* terrestrial */ + if (configure_tda827x_fe(dev, &md8800_dvbt_config, + &tda827x_cfg_0) < 0) + goto detach_frontend; + } else { /* satellite */ + fe0->dvb.frontend = dvb_attach(tda10086_attach, + &flydvbs, &dev->i2c_adap); + if (fe0->dvb.frontend) { + struct dvb_frontend *fe = fe0->dvb.frontend; + u8 dev_id = dev->eedata[2]; + u8 data = 0xc4; + struct i2c_msg msg = {.addr = 0x08, .flags = 0, .len = 1}; + + if (dvb_attach(tda826x_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, 0) == NULL) { + wprintk("%s: Medion Quadro, no tda826x " + "found !\n", __func__); + goto detach_frontend; + } + if (dev_id != 0x08) { + /* we need to open the i2c gate (we know it exists) */ + fe->ops.i2c_gate_ctrl(fe, 1); + if (dvb_attach(isl6405_attach, fe, + &dev->i2c_adap, 0x08, 0, 0) == NULL) { + wprintk("%s: Medion Quadro, no ISL6405 " + "found !\n", __func__); + goto detach_frontend; + } + if (dev_id == 0x07) { + /* fire up the 2nd section of the LNB supply since + we can't do this from the other section */ + msg.buf = &data; + i2c_transfer(&dev->i2c_adap, &msg, 1); + } + fe->ops.i2c_gate_ctrl(fe, 0); + dev->original_set_voltage = fe->ops.set_voltage; + fe->ops.set_voltage = md8800_set_voltage; + dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage; + fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage; + } else { + fe->ops.set_voltage = md8800_set_voltage2; + fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage2; + } + } + } + break; + case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180: + fe0->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180, + &dev->i2c_adap); + if (fe0->dvb.frontend) + dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x61, + NULL, DVB_PLL_TDHU2); + break; + case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI: + case SAA7134_BOARD_KWORLD_ATSC110: + fe0->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110, + &dev->i2c_adap); + if (fe0->dvb.frontend) + dvb_attach(simple_tuner_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x61, + TUNER_PHILIPS_TUV1236D); + break; + case SAA7134_BOARD_KWORLD_PC150U: + saa7134_set_gpio(dev, 18, 1); /* Switch to digital mode */ + saa7134_tuner_callback(dev, 0, + TDA18271_CALLBACK_CMD_AGC_ENABLE, 1); + fe0->dvb.frontend = dvb_attach(s5h1411_attach, + &kworld_s5h1411_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &kworld_pc150u_tda18271_config); + } + break; + case SAA7134_BOARD_FLYDVBS_LR300: + fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60, + &dev->i2c_adap, 0) == NULL) { + wprintk("%s: No tda826x found!\n", __func__); + goto detach_frontend; + } + if (dvb_attach(isl6421_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x08, 0, 0) == NULL) { + wprintk("%s: No ISL6421 found!\n", __func__); + goto detach_frontend; + } + } + break; + case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: + fe0->dvb.frontend = dvb_attach(tda10046_attach, + &medion_cardbus, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dev->original_demod_sleep = fe0->dvb.frontend->ops.sleep; + fe0->dvb.frontend->ops.sleep = philips_europa_demod_sleep; + + dvb_attach(simple_tuner_attach, fe0->dvb.frontend, + &dev->i2c_adap, medion_cardbus.tuner_address, + TUNER_PHILIPS_FMD1216ME_MK3); + } + break; + case SAA7134_BOARD_VIDEOMATE_DVBT_200A: + fe0->dvb.frontend = dvb_attach(tda10046_attach, + &philips_europa_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + fe0->dvb.frontend->ops.tuner_ops.init = philips_td1316_tuner_init; + fe0->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; + } + break; + case SAA7134_BOARD_CINERGY_HT_PCMCIA: + if (configure_tda827x_fe(dev, &cinergy_ht_config, + &tda827x_cfg_0) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_CINERGY_HT_PCI: + if (configure_tda827x_fe(dev, &cinergy_ht_pci_config, + &tda827x_cfg_0) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_PHILIPS_TIGER_S: + if (configure_tda827x_fe(dev, &philips_tiger_s_config, + &tda827x_cfg_2) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_ASUS_P7131_4871: + if (configure_tda827x_fe(dev, &asus_p7131_4871_config, + &tda827x_cfg_2) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: + if (configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config, + &tda827x_cfg_2) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_AVERMEDIA_SUPER_007: + if (configure_tda827x_fe(dev, &avermedia_super_007_config, + &tda827x_cfg_0) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_TWINHAN_DTV_DVB_3056: + if (configure_tda827x_fe(dev, &twinhan_dtv_dvb_3056_config, + &tda827x_cfg_2_sw42) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_PHILIPS_SNAKE: + fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + if (dvb_attach(tda826x_attach, fe0->dvb.frontend, 0x60, + &dev->i2c_adap, 0) == NULL) { + wprintk("%s: No tda826x found!\n", __func__); + goto detach_frontend; + } + if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0, 0) == NULL) { + wprintk("%s: No lnbp21 found!\n", __func__); + goto detach_frontend; + } + } + break; + case SAA7134_BOARD_CREATIX_CTX953: + if (configure_tda827x_fe(dev, &md8800_dvbt_config, + &tda827x_cfg_0) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_MSI_TVANYWHERE_AD11: + if (configure_tda827x_fe(dev, &philips_tiger_s_config, + &tda827x_cfg_2) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + dprintk("AverMedia E506R dvb setup\n"); + saa7134_set_gpio(dev, 25, 0); + msleep(10); + saa7134_set_gpio(dev, 25, 1); + fe0->dvb.frontend = dvb_attach(mt352_attach, + &avermedia_xc3028_mt352_dev, + &dev->i2c_adap); + attach_xc3028 = 1; + break; + case SAA7134_BOARD_MD7134_BRIDGE_2: + fe0->dvb.frontend = dvb_attach(tda10086_attach, + &sd1878_4m, &dev->i2c_adap); + if (fe0->dvb.frontend) { + struct dvb_frontend *fe; + if (dvb_attach(dvb_pll_attach, fe0->dvb.frontend, 0x60, + &dev->i2c_adap, DVB_PLL_PHILIPS_SD1878_TDA8261) == NULL) { + wprintk("%s: MD7134 DVB-S, no SD1878 " + "found !\n", __func__); + goto detach_frontend; + } + /* we need to open the i2c gate (we know it exists) */ + fe = fe0->dvb.frontend; + fe->ops.i2c_gate_ctrl(fe, 1); + if (dvb_attach(isl6405_attach, fe, + &dev->i2c_adap, 0x08, 0, 0) == NULL) { + wprintk("%s: MD7134 DVB-S, no ISL6405 " + "found !\n", __func__); + goto detach_frontend; + } + fe->ops.i2c_gate_ctrl(fe, 0); + dev->original_set_voltage = fe->ops.set_voltage; + fe->ops.set_voltage = md8800_set_voltage; + dev->original_set_high_voltage = fe->ops.enable_high_lnb_voltage; + fe->ops.enable_high_lnb_voltage = md8800_set_high_voltage; + } + break; + case SAA7134_BOARD_AVERMEDIA_M103: + saa7134_set_gpio(dev, 25, 0); + msleep(10); + saa7134_set_gpio(dev, 25, 1); + fe0->dvb.frontend = dvb_attach(mt352_attach, + &avermedia_xc3028_mt352_dev, + &dev->i2c_adap); + attach_xc3028 = 1; + break; + case SAA7134_BOARD_ASUSTeK_TIGER_3IN1: + if (!use_frontend) { /* terrestrial */ + if (configure_tda827x_fe(dev, &asus_tiger_3in1_config, + &tda827x_cfg_2) < 0) + goto detach_frontend; + } else { /* satellite */ + fe0->dvb.frontend = dvb_attach(tda10086_attach, + &flydvbs, &dev->i2c_adap); + if (fe0->dvb.frontend) { + if (dvb_attach(tda826x_attach, + fe0->dvb.frontend, 0x60, + &dev->i2c_adap, 0) == NULL) { + wprintk("%s: Asus Tiger 3in1, no " + "tda826x found!\n", __func__); + goto detach_frontend; + } + if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0, 0) == NULL) { + wprintk("%s: Asus Tiger 3in1, no lnbp21" + " found!\n", __func__); + goto detach_frontend; + } + } + } + break; + case SAA7134_BOARD_ASUSTeK_PS3_100: + if (!use_frontend) { /* terrestrial */ + if (configure_tda827x_fe(dev, &asus_ps3_100_config, + &tda827x_cfg_2) < 0) + goto detach_frontend; + } else { /* satellite */ + fe0->dvb.frontend = dvb_attach(tda10086_attach, + &flydvbs, &dev->i2c_adap); + if (fe0->dvb.frontend) { + if (dvb_attach(tda826x_attach, + fe0->dvb.frontend, 0x60, + &dev->i2c_adap, 0) == NULL) { + wprintk("%s: Asus My Cinema PS3-100, no " + "tda826x found!\n", __func__); + goto detach_frontend; + } + if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0, 0) == NULL) { + wprintk("%s: Asus My Cinema PS3-100, no lnbp21" + " found!\n", __func__); + goto detach_frontend; + } + } + } + break; + case SAA7134_BOARD_ASUSTeK_TIGER: + if (configure_tda827x_fe(dev, &philips_tiger_config, + &tda827x_cfg_0) < 0) + goto detach_frontend; + break; + case SAA7134_BOARD_BEHOLD_H6: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &behold_h6_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(simple_tuner_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x61, + TUNER_PHILIPS_FMD1216MEX_MK3); + } + break; + case SAA7134_BOARD_BEHOLD_X7: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &behold_x7_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(xc5000_attach, fe0->dvb.frontend, + &dev->i2c_adap, &behold_x7_tunerconfig); + } + break; + case SAA7134_BOARD_BEHOLD_H7: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &behold_x7_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(xc5000_attach, fe0->dvb.frontend, + &dev->i2c_adap, &behold_x7_tunerconfig); + } + break; + case SAA7134_BOARD_AVERMEDIA_A700_PRO: + case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: + /* Zarlink ZL10313 */ + fe0->dvb.frontend = dvb_attach(mt312_attach, + &avertv_a700_mt312, &dev->i2c_adap); + if (fe0->dvb.frontend) { + if (dvb_attach(zl10036_attach, fe0->dvb.frontend, + &avertv_a700_tuner, &dev->i2c_adap) == NULL) { + wprintk("%s: No zl10036 found!\n", + __func__); + } + } + break; + case SAA7134_BOARD_VIDEOMATE_S350: + fe0->dvb.frontend = dvb_attach(mt312_attach, + &zl10313_compro_s350_config, &dev->i2c_adap); + if (fe0->dvb.frontend) + if (dvb_attach(zl10039_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap) == NULL) + wprintk("%s: No zl10039 found!\n", + __func__); + + break; + case SAA7134_BOARD_VIDEOMATE_T750: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &videomate_t750_zl10353_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + if (dvb_attach(qt1010_attach, + fe0->dvb.frontend, + &dev->i2c_adap, + &videomate_t750_qt1010_config) == NULL) + wprintk("error attaching QT1010\n"); + } + break; + case SAA7134_BOARD_ZOLID_HYBRID_PCI: + fe0->dvb.frontend = dvb_attach(tda10048_attach, + &zolid_tda10048_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &zolid_tda18271_config); + } + break; + case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: + fe0->dvb.frontend = dvb_attach(tda10048_attach, + &dtv1000s_tda10048_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &dtv1000s_tda18271_config); + } + break; + case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG: + /* Switch to digital mode */ + saa7134_tuner_callback(dev, 0, + TDA18271_CALLBACK_CMD_AGC_ENABLE, 1); + fe0->dvb.frontend = dvb_attach(mb86a20s_attach, + &kworld_mb86a20s_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + fe0->dvb.frontend->ops.i2c_gate_ctrl = kworld_sbtvd_gate_ctrl; + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &kworld_tda18271_config); + } + + /* mb86a20s need to use the I2C gateway */ + break; + case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2: + fe0->dvb.frontend = dvb_attach(lgs8gxx_attach, + &prohdtv_pro2_lgs8g75_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &prohdtv_pro2_tda18271_config); + } + break; + default: + wprintk("Huh? unknown DVB card?\n"); + break; + } + + if (attach_xc3028) { + struct dvb_frontend *fe; + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_adap, + .i2c_addr = 0x61, + }; + + if (!fe0->dvb.frontend) + goto detach_frontend; + + fe = dvb_attach(xc2028_attach, fe0->dvb.frontend, &cfg); + if (!fe) { + printk(KERN_ERR "%s/2: xc3028 attach failed\n", + dev->name); + goto detach_frontend; + } + } + + if (NULL == fe0->dvb.frontend) { + printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name); + goto detach_frontend; + } + /* define general-purpose callback pointer */ + fe0->dvb.frontend->callback = saa7134_tuner_callback; + + /* register everything else */ + ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, + &dev->pci->dev, adapter_nr, 0); + + /* this sequence is necessary to make the tda1004x load its firmware + * and to enter analog mode of hybrid boards + */ + if (!ret) { + if (fe0->dvb.frontend->ops.init) + fe0->dvb.frontend->ops.init(fe0->dvb.frontend); + if (fe0->dvb.frontend->ops.sleep) + fe0->dvb.frontend->ops.sleep(fe0->dvb.frontend); + if (fe0->dvb.frontend->ops.tuner_ops.sleep) + fe0->dvb.frontend->ops.tuner_ops.sleep(fe0->dvb.frontend); + } + return ret; + +detach_frontend: + videobuf_dvb_dealloc_frontends(&dev->frontends); + return -EINVAL; +} + +static int dvb_fini(struct saa7134_dev *dev) +{ + struct videobuf_dvb_frontend *fe0; + + /* Get the first frontend */ + fe0 = videobuf_dvb_get_frontend(&dev->frontends, 1); + if (!fe0) + return -EINVAL; + + /* FIXME: I suspect that this code is bogus, since the entry for + Pinnacle 300I DVB-T PAL already defines the proper init to allow + the detection of mt2032 (TDA9887_PORT2_INACTIVE) + */ + if (dev->board == SAA7134_BOARD_PINNACLE_300I_DVBT_PAL) { + struct v4l2_priv_tun_config tda9887_cfg; + static int on = TDA9887_PRESENT | TDA9887_PORT2_INACTIVE; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &on; + + /* otherwise we don't detect the tuner on next insmod */ + saa_call_all(dev, tuner, s_config, &tda9887_cfg); + } else if (dev->board == SAA7134_BOARD_MEDION_MD8800_QUADRO) { + if ((dev->eedata[2] == 0x07) && use_frontend) { + /* turn off the 2nd lnb supply */ + u8 data = 0x80; + struct i2c_msg msg = {.addr = 0x08, .buf = &data, .flags = 0, .len = 1}; + struct dvb_frontend *fe; + fe = fe0->dvb.frontend; + if (fe->ops.i2c_gate_ctrl) { + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(&dev->i2c_adap, &msg, 1); + fe->ops.i2c_gate_ctrl(fe, 0); + } + } + } + videobuf_dvb_unregister_bus(&dev->frontends); + return 0; +} + +static struct saa7134_mpeg_ops dvb_ops = { + .type = SAA7134_MPEG_DVB, + .init = dvb_init, + .fini = dvb_fini, +}; + +static int __init dvb_register(void) +{ + return saa7134_ts_register(&dvb_ops); +} + +static void __exit dvb_unregister(void) +{ + saa7134_ts_unregister(&dvb_ops); +} + +module_init(dvb_register); +module_exit(dvb_unregister); + +/* ------------------------------------------------------------------ */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c new file mode 100644 index 000000000000..4df79c656909 --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -0,0 +1,590 @@ +/* + * + * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> + +#include "saa7134-reg.h" +#include "saa7134.h" + +#include <media/saa6752hs.h> +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> + +/* ------------------------------------------------------------------ */ + +MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); +MODULE_LICENSE("GPL"); + +static unsigned int empress_nr[] = {[0 ... (SAA7134_MAXBOARDS - 1)] = UNSET }; + +module_param_array(empress_nr, int, NULL, 0444); +MODULE_PARM_DESC(empress_nr,"ts device number"); + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug,"enable debug messages"); + +#define dprintk(fmt, arg...) if (debug) \ + printk(KERN_DEBUG "%s/empress: " fmt, dev->name , ## arg) + +/* ------------------------------------------------------------------ */ + +static void ts_reset_encoder(struct saa7134_dev* dev) +{ + if (!dev->empress_started) + return; + + saa_writeb(SAA7134_SPECIAL_MODE, 0x00); + msleep(10); + saa_writeb(SAA7134_SPECIAL_MODE, 0x01); + msleep(100); + dev->empress_started = 0; +} + +static int ts_init_encoder(struct saa7134_dev* dev) +{ + u32 leading_null_bytes = 0; + + /* If more cards start to need this, then this + should probably be added to the card definitions. */ + switch (dev->board) { + case SAA7134_BOARD_BEHOLD_M6: + case SAA7134_BOARD_BEHOLD_M63: + case SAA7134_BOARD_BEHOLD_M6_EXTRA: + leading_null_bytes = 1; + break; + } + ts_reset_encoder(dev); + saa_call_all(dev, core, init, leading_null_bytes); + dev->empress_started = 1; + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int ts_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct saa7134_dev *dev = video_drvdata(file); + int err; + + dprintk("open dev=%s\n", video_device_node_name(vdev)); + err = -EBUSY; + if (!mutex_trylock(&dev->empress_tsq.vb_lock)) + return err; + if (atomic_read(&dev->empress_users)) + goto done; + + /* Unmute audio */ + saa_writeb(SAA7134_AUDIO_MUTE_CTRL, + saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6)); + + atomic_inc(&dev->empress_users); + file->private_data = dev; + err = 0; + +done: + mutex_unlock(&dev->empress_tsq.vb_lock); + return err; +} + +static int ts_release(struct file *file) +{ + struct saa7134_dev *dev = file->private_data; + + videobuf_stop(&dev->empress_tsq); + videobuf_mmap_free(&dev->empress_tsq); + + /* stop the encoder */ + ts_reset_encoder(dev); + + /* Mute audio */ + saa_writeb(SAA7134_AUDIO_MUTE_CTRL, + saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6)); + + atomic_dec(&dev->empress_users); + + return 0; +} + +static ssize_t +ts_read(struct file *file, char __user *data, size_t count, loff_t *ppos) +{ + struct saa7134_dev *dev = file->private_data; + + if (!dev->empress_started) + ts_init_encoder(dev); + + return videobuf_read_stream(&dev->empress_tsq, + data, count, ppos, 0, + file->f_flags & O_NONBLOCK); +} + +static unsigned int +ts_poll(struct file *file, struct poll_table_struct *wait) +{ + struct saa7134_dev *dev = file->private_data; + + return videobuf_poll_stream(file, &dev->empress_tsq, wait); +} + + +static int +ts_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct saa7134_dev *dev = file->private_data; + + return videobuf_mmap_mapper(&dev->empress_tsq, vma); +} + +/* + * This function is _not_ called directly, but from + * video_generic_ioctl (and maybe others). userspace + * copying is done already, arg is a kernel pointer. + */ + +static int empress_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct saa7134_dev *dev = file->private_data; + + strcpy(cap->driver, "saa7134"); + strlcpy(cap->card, saa7134_boards[dev->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + return 0; +} + +static int empress_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(i->name, "CCIR656"); + + return 0; +} + +static int empress_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int empress_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + + return 0; +} + +static int empress_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index != 0) + return -EINVAL; + + strlcpy(f->description, "MPEG TS", sizeof(f->description)); + f->pixelformat = V4L2_PIX_FMT_MPEG; + + return 0; +} + +static int empress_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_dev *dev = file->private_data; + struct v4l2_mbus_framefmt mbus_fmt; + + saa_call_all(dev, video, g_mbus_fmt, &mbus_fmt); + + v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; + + return 0; +} + +static int empress_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_dev *dev = file->private_data; + struct v4l2_mbus_framefmt mbus_fmt; + + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, V4L2_MBUS_FMT_FIXED); + saa_call_all(dev, video, s_mbus_fmt, &mbus_fmt); + v4l2_fill_pix_format(&f->fmt.pix, &mbus_fmt); + + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; + + return 0; +} + +static int empress_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_dev *dev = file->private_data; + + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; + + return 0; +} + +static int empress_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct saa7134_dev *dev = file->private_data; + + return videobuf_reqbufs(&dev->empress_tsq, p); +} + +static int empress_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct saa7134_dev *dev = file->private_data; + + return videobuf_querybuf(&dev->empress_tsq, b); +} + +static int empress_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct saa7134_dev *dev = file->private_data; + + return videobuf_qbuf(&dev->empress_tsq, b); +} + +static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct saa7134_dev *dev = file->private_data; + + return videobuf_dqbuf(&dev->empress_tsq, b, + file->f_flags & O_NONBLOCK); +} + +static int empress_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct saa7134_dev *dev = file->private_data; + + return videobuf_streamon(&dev->empress_tsq); +} + +static int empress_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct saa7134_dev *dev = file->private_data; + + return videobuf_streamoff(&dev->empress_tsq); +} + +static int empress_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct saa7134_dev *dev = file->private_data; + int err; + + /* count == 0 is abused in saa6752hs.c, so that special + case is handled here explicitly. */ + if (ctrls->count == 0) + return 0; + + if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + + err = saa_call_empress(dev, core, s_ext_ctrls, ctrls); + ts_init_encoder(dev); + + return err; +} + +static int empress_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct saa7134_dev *dev = file->private_data; + + if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + return saa_call_empress(dev, core, g_ext_ctrls, ctrls); +} + +static int empress_g_ctrl(struct file *file, void *priv, + struct v4l2_control *c) +{ + struct saa7134_dev *dev = file->private_data; + + return saa7134_g_ctrl_internal(dev, NULL, c); +} + +static int empress_s_ctrl(struct file *file, void *priv, + struct v4l2_control *c) +{ + struct saa7134_dev *dev = file->private_data; + + return saa7134_s_ctrl_internal(dev, NULL, c); +} + +static int empress_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + /* Must be sorted from low to high control ID! */ + static const u32 user_ctrls[] = { + V4L2_CID_USER_CLASS, + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_AUDIO_VOLUME, + V4L2_CID_AUDIO_MUTE, + V4L2_CID_HFLIP, + 0 + }; + + /* Must be sorted from low to high control ID! */ + static const u32 mpeg_ctrls[] = { + V4L2_CID_MPEG_CLASS, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_CID_MPEG_STREAM_PID_PMT, + V4L2_CID_MPEG_STREAM_PID_AUDIO, + V4L2_CID_MPEG_STREAM_PID_VIDEO, + V4L2_CID_MPEG_STREAM_PID_PCR, + V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ, + V4L2_CID_MPEG_AUDIO_ENCODING, + V4L2_CID_MPEG_AUDIO_L2_BITRATE, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_CID_MPEG_VIDEO_BITRATE, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, + 0 + }; + static const u32 *ctrl_classes[] = { + user_ctrls, + mpeg_ctrls, + NULL + }; + struct saa7134_dev *dev = file->private_data; + + c->id = v4l2_ctrl_next(ctrl_classes, c->id); + if (c->id == 0) + return -EINVAL; + if (c->id == V4L2_CID_USER_CLASS || c->id == V4L2_CID_MPEG_CLASS) + return v4l2_ctrl_query_fill(c, 0, 0, 0, 0); + if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG) + return saa7134_queryctrl(file, priv, c); + return saa_call_empress(dev, core, queryctrl, c); +} + +static int empress_querymenu(struct file *file, void *priv, + struct v4l2_querymenu *c) +{ + struct saa7134_dev *dev = file->private_data; + + if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + return saa_call_empress(dev, core, querymenu, c); +} + +static int empress_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *chip) +{ + struct saa7134_dev *dev = file->private_data; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (chip->match.type == V4L2_CHIP_MATCH_I2C_DRIVER && + !strcmp(chip->match.name, "saa6752hs")) + return saa_call_empress(dev, core, g_chip_ident, chip); + if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR) + return saa_call_empress(dev, core, g_chip_ident, chip); + return -EINVAL; +} + +static int empress_s_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct saa7134_dev *dev = file->private_data; + + return saa7134_s_std_internal(dev, NULL, id); +} + +static int empress_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct saa7134_dev *dev = file->private_data; + + *id = dev->tvnorm->id; + return 0; +} + +static const struct v4l2_file_operations ts_fops = +{ + .owner = THIS_MODULE, + .open = ts_open, + .release = ts_release, + .read = ts_read, + .poll = ts_poll, + .mmap = ts_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops ts_ioctl_ops = { + .vidioc_querycap = empress_querycap, + .vidioc_enum_fmt_vid_cap = empress_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = empress_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = empress_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = empress_g_fmt_vid_cap, + .vidioc_reqbufs = empress_reqbufs, + .vidioc_querybuf = empress_querybuf, + .vidioc_qbuf = empress_qbuf, + .vidioc_dqbuf = empress_dqbuf, + .vidioc_streamon = empress_streamon, + .vidioc_streamoff = empress_streamoff, + .vidioc_s_ext_ctrls = empress_s_ext_ctrls, + .vidioc_g_ext_ctrls = empress_g_ext_ctrls, + .vidioc_enum_input = empress_enum_input, + .vidioc_g_input = empress_g_input, + .vidioc_s_input = empress_s_input, + .vidioc_queryctrl = empress_queryctrl, + .vidioc_querymenu = empress_querymenu, + .vidioc_g_ctrl = empress_g_ctrl, + .vidioc_s_ctrl = empress_s_ctrl, + .vidioc_g_chip_ident = empress_g_chip_ident, + .vidioc_s_std = empress_s_std, + .vidioc_g_std = empress_g_std, +}; + +/* ----------------------------------------------------------- */ + +static struct video_device saa7134_empress_template = { + .name = "saa7134-empress", + .fops = &ts_fops, + .ioctl_ops = &ts_ioctl_ops, + + .tvnorms = SAA7134_NORMS, + .current_norm = V4L2_STD_PAL, +}; + +static void empress_signal_update(struct work_struct *work) +{ + struct saa7134_dev* dev = + container_of(work, struct saa7134_dev, empress_workqueue); + + if (dev->nosignal) { + dprintk("no video signal\n"); + } else { + dprintk("video signal acquired\n"); + } +} + +static void empress_signal_change(struct saa7134_dev *dev) +{ + schedule_work(&dev->empress_workqueue); +} + + +static int empress_init(struct saa7134_dev *dev) +{ + int err; + + dprintk("%s: %s\n",dev->name,__func__); + dev->empress_dev = video_device_alloc(); + if (NULL == dev->empress_dev) + return -ENOMEM; + *(dev->empress_dev) = saa7134_empress_template; + dev->empress_dev->parent = &dev->pci->dev; + dev->empress_dev->release = video_device_release; + snprintf(dev->empress_dev->name, sizeof(dev->empress_dev->name), + "%s empress (%s)", dev->name, + saa7134_boards[dev->board].name); + + INIT_WORK(&dev->empress_workqueue, empress_signal_update); + + video_set_drvdata(dev->empress_dev, dev); + err = video_register_device(dev->empress_dev,VFL_TYPE_GRABBER, + empress_nr[dev->nr]); + if (err < 0) { + printk(KERN_INFO "%s: can't register video device\n", + dev->name); + video_device_release(dev->empress_dev); + dev->empress_dev = NULL; + return err; + } + printk(KERN_INFO "%s: registered device %s [mpeg]\n", + dev->name, video_device_node_name(dev->empress_dev)); + + videobuf_queue_sg_init(&dev->empress_tsq, &saa7134_ts_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_ALTERNATE, + sizeof(struct saa7134_buf), + dev, NULL); + + empress_signal_update(&dev->empress_workqueue); + return 0; +} + +static int empress_fini(struct saa7134_dev *dev) +{ + dprintk("%s: %s\n",dev->name,__func__); + + if (NULL == dev->empress_dev) + return 0; + flush_work(&dev->empress_workqueue); + video_unregister_device(dev->empress_dev); + dev->empress_dev = NULL; + return 0; +} + +static struct saa7134_mpeg_ops empress_ops = { + .type = SAA7134_MPEG_EMPRESS, + .init = empress_init, + .fini = empress_fini, + .signal_change = empress_signal_change, +}; + +static int __init empress_register(void) +{ + return saa7134_ts_register(&empress_ops); +} + +static void __exit empress_unregister(void) +{ + saa7134_ts_unregister(&empress_ops); +} + +module_init(empress_register); +module_exit(empress_unregister); + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/pci/saa7134/saa7134-i2c.c b/drivers/media/pci/saa7134/saa7134-i2c.c new file mode 100644 index 000000000000..a176ec3285e0 --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-i2c.c @@ -0,0 +1,435 @@ +/* + * + * device driver for philips saa7134 based TV cards + * i2c interface support + * + * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> + +#include "saa7134-reg.h" +#include "saa7134.h" +#include <media/v4l2-common.h> + +/* ----------------------------------------------------------- */ + +static unsigned int i2c_debug; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]"); + +static unsigned int i2c_scan; +module_param(i2c_scan, int, 0444); +MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time"); + +#define d1printk if (1 == i2c_debug) printk +#define d2printk if (2 == i2c_debug) printk + +#define I2C_WAIT_DELAY 32 +#define I2C_WAIT_RETRY 16 + +/* ----------------------------------------------------------- */ + +static char *str_i2c_status[] = { + "IDLE", "DONE_STOP", "BUSY", "TO_SCL", "TO_ARB", "DONE_WRITE", + "DONE_READ", "DONE_WRITE_TO", "DONE_READ_TO", "NO_DEVICE", + "NO_ACKN", "BUS_ERR", "ARB_LOST", "SEQ_ERR", "ST_ERR", "SW_ERR" +}; + +enum i2c_status { + IDLE = 0, // no I2C command pending + DONE_STOP = 1, // I2C command done and STOP executed + BUSY = 2, // executing I2C command + TO_SCL = 3, // executing I2C command, time out on clock stretching + TO_ARB = 4, // time out on arbitration trial, still trying + DONE_WRITE = 5, // I2C command done and awaiting next write command + DONE_READ = 6, // I2C command done and awaiting next read command + DONE_WRITE_TO = 7, // see 5, and time out on status echo + DONE_READ_TO = 8, // see 6, and time out on status echo + NO_DEVICE = 9, // no acknowledge on device slave address + NO_ACKN = 10, // no acknowledge after data byte transfer + BUS_ERR = 11, // bus error + ARB_LOST = 12, // arbitration lost during transfer + SEQ_ERR = 13, // erroneous programming sequence + ST_ERR = 14, // wrong status echoing + SW_ERR = 15 // software error +}; + +static char *str_i2c_attr[] = { + "NOP", "STOP", "CONTINUE", "START" +}; + +enum i2c_attr { + NOP = 0, // no operation on I2C bus + STOP = 1, // stop condition, no associated byte transfer + CONTINUE = 2, // continue with byte transfer + START = 3 // start condition with byte transfer +}; + +static inline enum i2c_status i2c_get_status(struct saa7134_dev *dev) +{ + enum i2c_status status; + + status = saa_readb(SAA7134_I2C_ATTR_STATUS) & 0x0f; + d2printk(KERN_DEBUG "%s: i2c stat <= %s\n",dev->name, + str_i2c_status[status]); + return status; +} + +static inline void i2c_set_status(struct saa7134_dev *dev, + enum i2c_status status) +{ + d2printk(KERN_DEBUG "%s: i2c stat => %s\n",dev->name, + str_i2c_status[status]); + saa_andorb(SAA7134_I2C_ATTR_STATUS,0x0f,status); +} + +static inline void i2c_set_attr(struct saa7134_dev *dev, enum i2c_attr attr) +{ + d2printk(KERN_DEBUG "%s: i2c attr => %s\n",dev->name, + str_i2c_attr[attr]); + saa_andorb(SAA7134_I2C_ATTR_STATUS,0xc0,attr << 6); +} + +static inline int i2c_is_error(enum i2c_status status) +{ + switch (status) { + case NO_DEVICE: + case NO_ACKN: + case BUS_ERR: + case ARB_LOST: + case SEQ_ERR: + case ST_ERR: + return true; + default: + return false; + } +} + +static inline int i2c_is_idle(enum i2c_status status) +{ + switch (status) { + case IDLE: + case DONE_STOP: + return true; + default: + return false; + } +} + +static inline int i2c_is_busy(enum i2c_status status) +{ + switch (status) { + case BUSY: + case TO_SCL: + case TO_ARB: + return true; + default: + return false; + } +} + +static int i2c_is_busy_wait(struct saa7134_dev *dev) +{ + enum i2c_status status; + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + status = i2c_get_status(dev); + if (!i2c_is_busy(status)) + break; + saa_wait(I2C_WAIT_DELAY); + } + if (I2C_WAIT_RETRY == count) + return false; + return true; +} + +static int i2c_reset(struct saa7134_dev *dev) +{ + enum i2c_status status; + int count; + + d2printk(KERN_DEBUG "%s: i2c reset\n",dev->name); + status = i2c_get_status(dev); + if (!i2c_is_error(status)) + return true; + i2c_set_status(dev,status); + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + status = i2c_get_status(dev); + if (!i2c_is_error(status)) + break; + udelay(I2C_WAIT_DELAY); + } + if (I2C_WAIT_RETRY == count) + return false; + + if (!i2c_is_idle(status)) + return false; + + i2c_set_attr(dev,NOP); + return true; +} + +static inline int i2c_send_byte(struct saa7134_dev *dev, + enum i2c_attr attr, + unsigned char data) +{ + enum i2c_status status; + __u32 dword; + + /* have to write both attr + data in one 32bit word */ + dword = saa_readl(SAA7134_I2C_ATTR_STATUS >> 2); + dword &= 0x0f; + dword |= (attr << 6); + dword |= ((__u32)data << 8); + dword |= 0x00 << 16; /* 100 kHz */ +// dword |= 0x40 << 16; /* 400 kHz */ + dword |= 0xf0 << 24; + saa_writel(SAA7134_I2C_ATTR_STATUS >> 2, dword); + d2printk(KERN_DEBUG "%s: i2c data => 0x%x\n",dev->name,data); + + if (!i2c_is_busy_wait(dev)) + return -EIO; + status = i2c_get_status(dev); + if (i2c_is_error(status)) + return -EIO; + return 0; +} + +static inline int i2c_recv_byte(struct saa7134_dev *dev) +{ + enum i2c_status status; + unsigned char data; + + i2c_set_attr(dev,CONTINUE); + if (!i2c_is_busy_wait(dev)) + return -EIO; + status = i2c_get_status(dev); + if (i2c_is_error(status)) + return -EIO; + data = saa_readb(SAA7134_I2C_DATA); + d2printk(KERN_DEBUG "%s: i2c data <= 0x%x\n",dev->name,data); + return data; +} + +static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + struct saa7134_dev *dev = i2c_adap->algo_data; + enum i2c_status status; + unsigned char data; + int addr,rc,i,byte; + + status = i2c_get_status(dev); + if (!i2c_is_idle(status)) + if (!i2c_reset(dev)) + return -EIO; + + d2printk("start xfer\n"); + d1printk(KERN_DEBUG "%s: i2c xfer:",dev->name); + for (i = 0; i < num; i++) { + if (!(msgs[i].flags & I2C_M_NOSTART) || 0 == i) { + /* send address */ + d2printk("send address\n"); + addr = msgs[i].addr << 1; + if (msgs[i].flags & I2C_M_RD) + addr |= 1; + if (i > 0 && msgs[i].flags & + I2C_M_RD && msgs[i].addr != 0x40 && + msgs[i].addr != 0x19) { + /* workaround for a saa7134 i2c bug + * needed to talk to the mt352 demux + * thanks to pinnacle for the hint */ + int quirk = 0xfe; + d1printk(" [%02x quirk]",quirk); + i2c_send_byte(dev,START,quirk); + i2c_recv_byte(dev); + } + d1printk(" < %02x", addr); + rc = i2c_send_byte(dev,START,addr); + if (rc < 0) + goto err; + } + if (msgs[i].flags & I2C_M_RD) { + /* read bytes */ + d2printk("read bytes\n"); + for (byte = 0; byte < msgs[i].len; byte++) { + d1printk(" ="); + rc = i2c_recv_byte(dev); + if (rc < 0) + goto err; + d1printk("%02x", rc); + msgs[i].buf[byte] = rc; + } + /* discard mysterious extra byte when reading + from Samsung S5H1411. i2c bus gets error + if we do not. */ + if (0x19 == msgs[i].addr) { + d1printk(" ?"); + rc = i2c_recv_byte(dev); + if (rc < 0) + goto err; + d1printk("%02x", rc); + } + } else { + /* write bytes */ + d2printk("write bytes\n"); + for (byte = 0; byte < msgs[i].len; byte++) { + data = msgs[i].buf[byte]; + d1printk(" %02x", data); + rc = i2c_send_byte(dev,CONTINUE,data); + if (rc < 0) + goto err; + } + } + } + d2printk("xfer done\n"); + d1printk(" >"); + i2c_set_attr(dev,STOP); + rc = -EIO; + if (!i2c_is_busy_wait(dev)) + goto err; + status = i2c_get_status(dev); + if (i2c_is_error(status)) + goto err; + /* ensure that the bus is idle for at least one bit slot */ + msleep(1); + + d1printk("\n"); + return num; + err: + if (1 == i2c_debug) { + status = i2c_get_status(dev); + printk(" ERROR: %s\n",str_i2c_status[status]); + } + return rc; +} + +/* ----------------------------------------------------------- */ + +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm saa7134_algo = { + .master_xfer = saa7134_i2c_xfer, + .functionality = functionality, +}; + +static struct i2c_adapter saa7134_adap_template = { + .owner = THIS_MODULE, + .name = "saa7134", + .algo = &saa7134_algo, +}; + +static struct i2c_client saa7134_client_template = { + .name = "saa7134 internal", +}; + +/* ----------------------------------------------------------- */ + +static int +saa7134_i2c_eeprom(struct saa7134_dev *dev, unsigned char *eedata, int len) +{ + unsigned char buf; + int i,err; + + dev->i2c_client.addr = 0xa0 >> 1; + buf = 0; + if (1 != (err = i2c_master_send(&dev->i2c_client,&buf,1))) { + printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", + dev->name,err); + return -1; + } + if (len != (err = i2c_master_recv(&dev->i2c_client,eedata,len))) { + printk(KERN_WARNING "%s: i2c eeprom read error (err=%d)\n", + dev->name,err); + return -1; + } + for (i = 0; i < len; i++) { + if (0 == (i % 16)) + printk(KERN_INFO "%s: i2c eeprom %02x:",dev->name,i); + printk(" %02x",eedata[i]); + if (15 == (i % 16)) + printk("\n"); + } + return 0; +} + +static char *i2c_devs[128] = { + [ 0x20 ] = "mpeg encoder (saa6752hs)", + [ 0xa0 >> 1 ] = "eeprom", + [ 0xc0 >> 1 ] = "tuner (analog)", + [ 0x86 >> 1 ] = "tda9887", + [ 0x5a >> 1 ] = "remote control", +}; + +static void do_i2c_scan(char *name, struct i2c_client *c) +{ + unsigned char buf; + int i,rc; + + for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { + c->addr = i; + rc = i2c_master_recv(c,&buf,0); + if (rc < 0) + continue; + printk("%s: i2c scan: found device @ 0x%x [%s]\n", + name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???"); + } +} + +int saa7134_i2c_register(struct saa7134_dev *dev) +{ + dev->i2c_adap = saa7134_adap_template; + dev->i2c_adap.dev.parent = &dev->pci->dev; + strcpy(dev->i2c_adap.name,dev->name); + dev->i2c_adap.algo_data = dev; + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); + i2c_add_adapter(&dev->i2c_adap); + + dev->i2c_client = saa7134_client_template; + dev->i2c_client.adapter = &dev->i2c_adap; + + saa7134_i2c_eeprom(dev,dev->eedata,sizeof(dev->eedata)); + if (i2c_scan) + do_i2c_scan(dev->name,&dev->i2c_client); + + /* Instantiate the IR receiver device, if present */ + saa7134_probe_i2c_ir(dev); + return 0; +} + +int saa7134_i2c_unregister(struct saa7134_dev *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/pci/saa7134/saa7134-input.c b/drivers/media/pci/saa7134/saa7134-input.c new file mode 100644 index 000000000000..0f78f5e537e2 --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-input.c @@ -0,0 +1,1041 @@ +/* + * + * handle saa7134 IR remotes via linux kernel input layer. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/slab.h> + +#include "saa7134-reg.h" +#include "saa7134.h" + +#define MODULE_NAME "saa7134" + +static unsigned int disable_ir; +module_param(disable_ir, int, 0444); +MODULE_PARM_DESC(disable_ir,"disable infrared remote support"); + +static unsigned int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]"); + +static int pinnacle_remote; +module_param(pinnacle_remote, int, 0644); /* Choose Pinnacle PCTV remote */ +MODULE_PARM_DESC(pinnacle_remote, "Specify Pinnacle PCTV remote: 0=coloured, 1=grey (defaults to 0)"); + +#define dprintk(fmt, arg...) if (ir_debug) \ + printk(KERN_DEBUG "%s/ir: " fmt, dev->name , ## arg) +#define i2cdprintk(fmt, arg...) if (ir_debug) \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg) + +/* Helper function for raw decoding at GPIO16 or GPIO18 */ +static int saa7134_raw_decode_irq(struct saa7134_dev *dev); + +/* -------------------- GPIO generic keycode builder -------------------- */ + +static int build_key(struct saa7134_dev *dev) +{ + struct saa7134_card_ir *ir = dev->remote; + u32 gpio, data; + + /* here comes the additional handshake steps for some cards */ + switch (dev->board) { + case SAA7134_BOARD_GOTVIEW_7135: + saa_setb(SAA7134_GPIO_GPSTATUS1, 0x80); + saa_clearb(SAA7134_GPIO_GPSTATUS1, 0x80); + break; + } + /* rising SAA7134_GPIO_GPRESCAN reads the status */ + saa_clearb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN); + + gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); + if (ir->polling) { + if (ir->last_gpio == gpio) + return 0; + ir->last_gpio = gpio; + } + + data = ir_extract_bits(gpio, ir->mask_keycode); + dprintk("build_key gpio=0x%x mask=0x%x data=%d\n", + gpio, ir->mask_keycode, data); + + switch (dev->board) { + case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: + if (data == ir->mask_keycode) + rc_keyup(ir->dev); + else + rc_keydown_notimeout(ir->dev, data, 0); + return 0; + } + + if (ir->polling) { + if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || + (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { + rc_keydown_notimeout(ir->dev, data, 0); + } else { + rc_keyup(ir->dev); + } + } + else { /* IRQ driven mode - handle key press and release in one go */ + if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || + (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { + rc_keydown_notimeout(ir->dev, data, 0); + rc_keyup(ir->dev); + } + } + + return 0; +} + +/* --------------------- Chip specific I2C key builders ----------------- */ + +static int get_key_flydvb_trio(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + int gpio; + int attempt = 0; + unsigned char b; + + /* We need this to access GPI Used by the saa_readl macro. */ + struct saa7134_dev *dev = ir->c->adapter->algo_data; + + if (dev == NULL) { + i2cdprintk("get_key_flydvb_trio: " + "ir->c->adapter->algo_data is NULL!\n"); + return -EIO; + } + + /* rising SAA7134_GPIGPRESCAN reads the status */ + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + + gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); + + if (0x40000 & ~gpio) + return 0; /* No button press */ + + /* No button press - only before first key pressed */ + if (b == 0xFF) + return 0; + + /* poll IR chip */ + /* weak up the IR chip */ + b = 0; + + while (1 != i2c_master_send(ir->c, &b, 1)) { + if ((attempt++) < 10) { + /* + * wait a bit for next attempt - + * I don't know how make it better + */ + msleep(10); + continue; + } + i2cdprintk("send wake up byte to pic16C505 (IR chip)" + "failed %dx\n", attempt); + return -EIO; + } + if (1 != i2c_master_recv(ir->c, &b, 1)) { + i2cdprintk("read error\n"); + return -EIO; + } + + *ir_key = b; + *ir_raw = b; + return 1; +} + +static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw) +{ + unsigned char b; + int gpio; + + /* <dev> is needed to access GPIO. Used by the saa_readl macro. */ + struct saa7134_dev *dev = ir->c->adapter->algo_data; + if (dev == NULL) { + i2cdprintk("get_key_msi_tvanywhere_plus: " + "ir->c->adapter->algo_data is NULL!\n"); + return -EIO; + } + + /* rising SAA7134_GPIO_GPRESCAN reads the status */ + + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + + gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); + + /* GPIO&0x40 is pulsed low when a button is pressed. Don't do + I2C receive if gpio&0x40 is not low. */ + + if (gpio & 0x40) + return 0; /* No button press */ + + /* GPIO says there is a button press. Get it. */ + + if (1 != i2c_master_recv(ir->c, &b, 1)) { + i2cdprintk("read error\n"); + return -EIO; + } + + /* No button press */ + + if (b == 0xff) + return 0; + + /* Button pressed */ + + dprintk("get_key_msi_tvanywhere_plus: Key = 0x%02X\n", b); + *ir_key = b; + *ir_raw = b; + return 1; +} + +/* copied and modified from get_key_msi_tvanywhere_plus() */ +static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw) +{ + unsigned char b; + unsigned int gpio; + + /* <dev> is needed to access GPIO. Used by the saa_readl macro. */ + struct saa7134_dev *dev = ir->c->adapter->algo_data; + if (dev == NULL) { + i2cdprintk("get_key_kworld_pc150u: " + "ir->c->adapter->algo_data is NULL!\n"); + return -EIO; + } + + /* rising SAA7134_GPIO_GPRESCAN reads the status */ + + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + + gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); + + /* GPIO&0x100 is pulsed low when a button is pressed. Don't do + I2C receive if gpio&0x100 is not low. */ + + if (gpio & 0x100) + return 0; /* No button press */ + + /* GPIO says there is a button press. Get it. */ + + if (1 != i2c_master_recv(ir->c, &b, 1)) { + i2cdprintk("read error\n"); + return -EIO; + } + + /* No button press */ + + if (b == 0xff) + return 0; + + /* Button pressed */ + + dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b); + *ir_key = b; + *ir_raw = b; + return 1; +} + +static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char b; + + /* poll IR chip */ + if (1 != i2c_master_recv(ir->c, &b, 1)) { + i2cdprintk("read error\n"); + return -EIO; + } + + /* no button press */ + if (b==0) + return 0; + + /* repeating */ + if (b & 0x80) + return 1; + + *ir_key = b; + *ir_raw = b; + return 1; +} + +static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char buf[5]; + + /* poll IR chip */ + if (5 != i2c_master_recv(ir->c, buf, 5)) + return -EIO; + + /* Check if some key were pressed */ + if (!(buf[0] & 0x80)) + return 0; + + /* + * buf[3] & 0x80 is always high. + * buf[3] & 0x40 is a parity bit. A repeat event is marked + * by preserving it into two separate readings + * buf[4] bits 0 and 1, and buf[1] and buf[2] are always + * zero. + */ + *ir_key = 0x1fff & ((buf[3] << 8) | (buf[4] >> 2)); + *ir_raw = *ir_key; + return 1; +} + + +static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char data[12]; + u32 gpio; + + struct saa7134_dev *dev = ir->c->adapter->algo_data; + + /* rising SAA7134_GPIO_GPRESCAN reads the status */ + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + + gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2); + + if (0x400000 & ~gpio) + return 0; /* No button press */ + + ir->c->addr = 0x5a >> 1; + + if (12 != i2c_master_recv(ir->c, data, 12)) { + i2cdprintk("read error\n"); + return -EIO; + } + + if (data[9] != (unsigned char)(~data[8])) + return 0; + + *ir_raw = ((data[10] << 16) | (data[11] << 8) | (data[9] << 0)); + *ir_key = *ir_raw; + + return 1; +} + +/* Common (grey or coloured) pinnacle PCTV remote handling + * + */ +static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, + int parity_offset, int marker, int code_modulo) +{ + unsigned char b[4]; + unsigned int start = 0,parity = 0,code = 0; + + /* poll IR chip */ + if (4 != i2c_master_recv(ir->c, b, 4)) { + i2cdprintk("read error\n"); + return -EIO; + } + + for (start = 0; start < ARRAY_SIZE(b); start++) { + if (b[start] == marker) { + code=b[(start+parity_offset + 1) % 4]; + parity=b[(start+parity_offset) % 4]; + } + } + + /* Empty Request */ + if (parity == 0) + return 0; + + /* Repeating... */ + if (ir->old == parity) + return 0; + + ir->old = parity; + + /* drop special codes when a key is held down a long time for the grey controller + In this case, the second bit of the code is asserted */ + if (marker == 0xfe && (code & 0x40)) + return 0; + + code %= code_modulo; + + *ir_raw = code; + *ir_key = code; + + i2cdprintk("Pinnacle PCTV key %02x\n", code); + + return 1; +} + +/* The grey pinnacle PCTV remote + * + * There are one issue with this remote: + * - I2c packet does not change when the same key is pressed quickly. The workaround + * is to hold down each key for about half a second, so that another code is generated + * in the i2c packet, and the function can distinguish key presses. + * + * Sylvain Pasche <sylvain.pasche@gmail.com> + */ +static int get_key_pinnacle_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + + return get_key_pinnacle(ir, ir_key, ir_raw, 1, 0xfe, 0xff); +} + + +/* The new pinnacle PCTV remote (with the colored buttons) + * + * Ricardo Cerqueira <v4l@cerqueira.org> + */ +static int get_key_pinnacle_color(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + /* code_modulo parameter (0x88) is used to reduce code value to fit inside IR_KEYTAB_SIZE + * + * this is the only value that results in 42 unique + * codes < 128 + */ + + return get_key_pinnacle(ir, ir_key, ir_raw, 2, 0x80, 0x88); +} + +void saa7134_input_irq(struct saa7134_dev *dev) +{ + struct saa7134_card_ir *ir; + + if (!dev || !dev->remote) + return; + + ir = dev->remote; + if (!ir->running) + return; + + if (!ir->polling && !ir->raw_decode) { + build_key(dev); + } else if (ir->raw_decode) { + saa7134_raw_decode_irq(dev); + } +} + +static void saa7134_input_timer(unsigned long data) +{ + struct saa7134_dev *dev = (struct saa7134_dev *)data; + struct saa7134_card_ir *ir = dev->remote; + + build_key(dev); + mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); +} + +static void ir_raw_decode_timer_end(unsigned long data) +{ + struct saa7134_dev *dev = (struct saa7134_dev *)data; + + ir_raw_event_handle(dev->remote->dev); +} + +static int __saa7134_ir_start(void *priv) +{ + struct saa7134_dev *dev = priv; + struct saa7134_card_ir *ir; + + if (!dev || !dev->remote) + return -EINVAL; + + ir = dev->remote; + if (ir->running) + return 0; + + /* Moved here from saa7134_input_init1() because the latter + * is not called on device resume */ + switch (dev->board) { + case SAA7134_BOARD_MD2819: + case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: + case SAA7134_BOARD_AVERMEDIA_305: + case SAA7134_BOARD_AVERMEDIA_307: + case SAA7134_BOARD_AVERMEDIA_STUDIO_305: + case SAA7134_BOARD_AVERMEDIA_STUDIO_505: + case SAA7134_BOARD_AVERMEDIA_STUDIO_307: + case SAA7134_BOARD_AVERMEDIA_STUDIO_507: + case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA: + case SAA7134_BOARD_AVERMEDIA_GO_007_FM: + case SAA7134_BOARD_AVERMEDIA_M102: + case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: + /* Without this we won't receive key up events */ + saa_setb(SAA7134_GPIO_GPMODE0, 0x4); + saa_setb(SAA7134_GPIO_GPSTATUS0, 0x4); + break; + case SAA7134_BOARD_AVERMEDIA_777: + case SAA7134_BOARD_AVERMEDIA_A16AR: + /* Without this we won't receive key up events */ + saa_setb(SAA7134_GPIO_GPMODE1, 0x1); + saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); + break; + case SAA7134_BOARD_AVERMEDIA_A16D: + /* Without this we won't receive key up events */ + saa_setb(SAA7134_GPIO_GPMODE1, 0x1); + saa_setb(SAA7134_GPIO_GPSTATUS1, 0x1); + break; + case SAA7134_BOARD_GOTVIEW_7135: + saa_setb(SAA7134_GPIO_GPMODE1, 0x80); + break; + } + + ir->running = true; + + if (ir->polling) { + setup_timer(&ir->timer, saa7134_input_timer, + (unsigned long)dev); + ir->timer.expires = jiffies + HZ; + add_timer(&ir->timer); + } else if (ir->raw_decode) { + /* set timer_end for code completion */ + setup_timer(&ir->timer, ir_raw_decode_timer_end, + (unsigned long)dev); + } + + return 0; +} + +static void __saa7134_ir_stop(void *priv) +{ + struct saa7134_dev *dev = priv; + struct saa7134_card_ir *ir; + + if (!dev || !dev->remote) + return; + + ir = dev->remote; + if (!ir->running) + return; + + if (ir->polling || ir->raw_decode) + del_timer_sync(&ir->timer); + + ir->running = false; + + return; +} + +int saa7134_ir_start(struct saa7134_dev *dev) +{ + if (dev->remote->users) + return __saa7134_ir_start(dev); + + return 0; +} + +void saa7134_ir_stop(struct saa7134_dev *dev) +{ + if (dev->remote->users) + __saa7134_ir_stop(dev); +} + +static int saa7134_ir_open(struct rc_dev *rc) +{ + struct saa7134_dev *dev = rc->priv; + + dev->remote->users++; + return __saa7134_ir_start(dev); +} + +static void saa7134_ir_close(struct rc_dev *rc) +{ + struct saa7134_dev *dev = rc->priv; + + dev->remote->users--; + if (!dev->remote->users) + __saa7134_ir_stop(dev); +} + +int saa7134_input_init1(struct saa7134_dev *dev) +{ + struct saa7134_card_ir *ir; + struct rc_dev *rc; + char *ir_codes = NULL; + u32 mask_keycode = 0; + u32 mask_keydown = 0; + u32 mask_keyup = 0; + unsigned polling = 0; + bool raw_decode = false; + int err; + + if (dev->has_remote != SAA7134_REMOTE_GPIO) + return -ENODEV; + if (disable_ir) + return -ENODEV; + + /* detect & configure */ + switch (dev->board) { + case SAA7134_BOARD_FLYVIDEO2000: + case SAA7134_BOARD_FLYVIDEO3000: + case SAA7134_BOARD_FLYTVPLATINUM_FM: + case SAA7134_BOARD_FLYTVPLATINUM_MINI2: + case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: + ir_codes = RC_MAP_FLYVIDEO; + mask_keycode = 0xEC00000; + mask_keydown = 0x0040000; + break; + case SAA7134_BOARD_CINERGY400: + case SAA7134_BOARD_CINERGY600: + case SAA7134_BOARD_CINERGY600_MK3: + ir_codes = RC_MAP_CINERGY; + mask_keycode = 0x00003f; + mask_keyup = 0x040000; + break; + case SAA7134_BOARD_ECS_TVP3XP: + case SAA7134_BOARD_ECS_TVP3XP_4CB5: + ir_codes = RC_MAP_EZTV; + mask_keycode = 0x00017c; + mask_keyup = 0x000002; + polling = 50; // ms + break; + case SAA7134_BOARD_KWORLD_XPERT: + case SAA7134_BOARD_AVACSSMARTTV: + ir_codes = RC_MAP_PIXELVIEW; + mask_keycode = 0x00001F; + mask_keyup = 0x000020; + polling = 50; // ms + break; + case SAA7134_BOARD_MD2819: + case SAA7134_BOARD_KWORLD_VSTREAM_XPERT: + case SAA7134_BOARD_AVERMEDIA_305: + case SAA7134_BOARD_AVERMEDIA_307: + case SAA7134_BOARD_AVERMEDIA_STUDIO_305: + case SAA7134_BOARD_AVERMEDIA_STUDIO_505: + case SAA7134_BOARD_AVERMEDIA_STUDIO_307: + case SAA7134_BOARD_AVERMEDIA_STUDIO_507: + case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA: + case SAA7134_BOARD_AVERMEDIA_GO_007_FM: + case SAA7134_BOARD_AVERMEDIA_M102: + case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: + ir_codes = RC_MAP_AVERMEDIA; + mask_keycode = 0x0007C8; + mask_keydown = 0x000010; + polling = 50; // ms + /* GPIO stuff moved to __saa7134_ir_start() */ + break; + case SAA7134_BOARD_AVERMEDIA_M135A: + ir_codes = RC_MAP_AVERMEDIA_M135A; + mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ + mask_keyup = 0x0040000; + mask_keycode = 0xffff; + raw_decode = true; + break; + case SAA7134_BOARD_AVERMEDIA_M733A: + ir_codes = RC_MAP_AVERMEDIA_M733A_RM_K6; + mask_keydown = 0x0040000; + mask_keyup = 0x0040000; + mask_keycode = 0xffff; + raw_decode = true; + break; + case SAA7134_BOARD_AVERMEDIA_777: + case SAA7134_BOARD_AVERMEDIA_A16AR: + ir_codes = RC_MAP_AVERMEDIA; + mask_keycode = 0x02F200; + mask_keydown = 0x000400; + polling = 50; // ms + /* GPIO stuff moved to __saa7134_ir_start() */ + break; + case SAA7134_BOARD_AVERMEDIA_A16D: + ir_codes = RC_MAP_AVERMEDIA_A16D; + mask_keycode = 0x02F200; + mask_keydown = 0x000400; + polling = 50; /* ms */ + /* GPIO stuff moved to __saa7134_ir_start() */ + break; + case SAA7134_BOARD_KWORLD_TERMINATOR: + ir_codes = RC_MAP_PIXELVIEW; + mask_keycode = 0x00001f; + mask_keyup = 0x000060; + polling = 50; // ms + break; + case SAA7134_BOARD_MANLI_MTV001: + case SAA7134_BOARD_MANLI_MTV002: + ir_codes = RC_MAP_MANLI; + mask_keycode = 0x001f00; + mask_keyup = 0x004000; + polling = 50; /* ms */ + break; + case SAA7134_BOARD_BEHOLD_409FM: + case SAA7134_BOARD_BEHOLD_401: + case SAA7134_BOARD_BEHOLD_403: + case SAA7134_BOARD_BEHOLD_403FM: + case SAA7134_BOARD_BEHOLD_405: + case SAA7134_BOARD_BEHOLD_405FM: + case SAA7134_BOARD_BEHOLD_407: + case SAA7134_BOARD_BEHOLD_407FM: + case SAA7134_BOARD_BEHOLD_409: + case SAA7134_BOARD_BEHOLD_505FM: + case SAA7134_BOARD_BEHOLD_505RDS_MK5: + case SAA7134_BOARD_BEHOLD_505RDS_MK3: + case SAA7134_BOARD_BEHOLD_507_9FM: + case SAA7134_BOARD_BEHOLD_507RDS_MK3: + case SAA7134_BOARD_BEHOLD_507RDS_MK5: + ir_codes = RC_MAP_MANLI; + mask_keycode = 0x003f00; + mask_keyup = 0x004000; + polling = 50; /* ms */ + break; + case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: + ir_codes = RC_MAP_BEHOLD_COLUMBUS; + mask_keycode = 0x003f00; + mask_keyup = 0x004000; + polling = 50; // ms + break; + case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: + ir_codes = RC_MAP_PCTV_SEDNA; + mask_keycode = 0x001f00; + mask_keyup = 0x004000; + polling = 50; // ms + break; + case SAA7134_BOARD_GOTVIEW_7135: + ir_codes = RC_MAP_GOTVIEW7135; + mask_keycode = 0x0003CC; + mask_keydown = 0x000010; + polling = 5; /* ms */ + /* GPIO stuff moved to __saa7134_ir_start() */ + break; + case SAA7134_BOARD_VIDEOMATE_TV_PVR: + case SAA7134_BOARD_VIDEOMATE_GOLD_PLUS: + case SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII: + ir_codes = RC_MAP_VIDEOMATE_TV_PVR; + mask_keycode = 0x00003F; + mask_keyup = 0x400000; + polling = 50; // ms + break; + case SAA7134_BOARD_PROTEUS_2309: + ir_codes = RC_MAP_PROTEUS_2309; + mask_keycode = 0x00007F; + mask_keyup = 0x000080; + polling = 50; // ms + break; + case SAA7134_BOARD_VIDEOMATE_DVBT_300: + case SAA7134_BOARD_VIDEOMATE_DVBT_200: + ir_codes = RC_MAP_VIDEOMATE_TV_PVR; + mask_keycode = 0x003F00; + mask_keyup = 0x040000; + break; + case SAA7134_BOARD_FLYDVBS_LR300: + case SAA7134_BOARD_FLYDVBT_LR301: + case SAA7134_BOARD_FLYDVBTDUO: + ir_codes = RC_MAP_FLYDVB; + mask_keycode = 0x0001F00; + mask_keydown = 0x0040000; + break; + case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: + case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: + ir_codes = RC_MAP_ASUS_PC39; + mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ + mask_keyup = 0x0040000; + mask_keycode = 0xffff; + raw_decode = true; + break; + case SAA7134_BOARD_ASUSTeK_PS3_100: + ir_codes = RC_MAP_ASUS_PS3_100; + mask_keydown = 0x0040000; + mask_keyup = 0x0040000; + mask_keycode = 0xffff; + raw_decode = true; + break; + case SAA7134_BOARD_ENCORE_ENLTV: + case SAA7134_BOARD_ENCORE_ENLTV_FM: + ir_codes = RC_MAP_ENCORE_ENLTV; + mask_keycode = 0x00007f; + mask_keyup = 0x040000; + polling = 50; // ms + break; + case SAA7134_BOARD_ENCORE_ENLTV_FM53: + case SAA7134_BOARD_ENCORE_ENLTV_FM3: + ir_codes = RC_MAP_ENCORE_ENLTV_FM53; + mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ + mask_keyup = 0x0040000; + mask_keycode = 0xffff; + raw_decode = true; + break; + case SAA7134_BOARD_10MOONSTVMASTER3: + ir_codes = RC_MAP_ENCORE_ENLTV; + mask_keycode = 0x5f80000; + mask_keyup = 0x8000000; + polling = 50; //ms + break; + case SAA7134_BOARD_GENIUS_TVGO_A11MCE: + ir_codes = RC_MAP_GENIUS_TVGO_A11MCE; + mask_keycode = 0xff; + mask_keydown = 0xf00000; + polling = 50; /* ms */ + break; + case SAA7134_BOARD_REAL_ANGEL_220: + ir_codes = RC_MAP_REAL_AUDIO_220_32_KEYS; + mask_keycode = 0x3f00; + mask_keyup = 0x4000; + polling = 50; /* ms */ + break; + case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: + ir_codes = RC_MAP_KWORLD_PLUS_TV_ANALOG; + mask_keycode = 0x7f; + polling = 40; /* ms */ + break; + case SAA7134_BOARD_VIDEOMATE_S350: + ir_codes = RC_MAP_VIDEOMATE_S350; + mask_keycode = 0x003f00; + mask_keydown = 0x040000; + break; + case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: + ir_codes = RC_MAP_WINFAST; + mask_keycode = 0x5f00; + mask_keyup = 0x020000; + polling = 50; /* ms */ + break; + case SAA7134_BOARD_VIDEOMATE_M1F: + ir_codes = RC_MAP_VIDEOMATE_K100; + mask_keycode = 0x0ff00; + mask_keyup = 0x040000; + break; + case SAA7134_BOARD_HAUPPAUGE_HVR1150: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: + ir_codes = RC_MAP_HAUPPAUGE; + mask_keydown = 0x0040000; /* Enable GPIO18 line on both edges */ + mask_keyup = 0x0040000; + mask_keycode = 0xffff; + raw_decode = true; + break; + } + if (NULL == ir_codes) { + printk("%s: Oops: IR config error [card=%d]\n", + dev->name, dev->board); + return -ENODEV; + } + + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + rc = rc_allocate_device(); + if (!ir || !rc) { + err = -ENOMEM; + goto err_out_free; + } + + ir->dev = rc; + dev->remote = ir; + + /* init hardware-specific stuff */ + ir->mask_keycode = mask_keycode; + ir->mask_keydown = mask_keydown; + ir->mask_keyup = mask_keyup; + ir->polling = polling; + ir->raw_decode = raw_decode; + + /* init input device */ + snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)", + saa7134_boards[dev->board].name); + snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", + pci_name(dev->pci)); + + rc->priv = dev; + rc->open = saa7134_ir_open; + rc->close = saa7134_ir_close; + if (raw_decode) + rc->driver_type = RC_DRIVER_IR_RAW; + + rc->input_name = ir->name; + rc->input_phys = ir->phys; + rc->input_id.bustype = BUS_PCI; + rc->input_id.version = 1; + if (dev->pci->subsystem_vendor) { + rc->input_id.vendor = dev->pci->subsystem_vendor; + rc->input_id.product = dev->pci->subsystem_device; + } else { + rc->input_id.vendor = dev->pci->vendor; + rc->input_id.product = dev->pci->device; + } + rc->dev.parent = &dev->pci->dev; + rc->map_name = ir_codes; + rc->driver_name = MODULE_NAME; + + err = rc_register_device(rc); + if (err) + goto err_out_free; + + return 0; + +err_out_free: + rc_free_device(rc); + dev->remote = NULL; + kfree(ir); + return err; +} + +void saa7134_input_fini(struct saa7134_dev *dev) +{ + if (NULL == dev->remote) + return; + + saa7134_ir_stop(dev); + rc_unregister_device(dev->remote->dev); + kfree(dev->remote); + dev->remote = NULL; +} + +void saa7134_probe_i2c_ir(struct saa7134_dev *dev) +{ + struct i2c_board_info info; + struct i2c_msg msg_msi = { + .addr = 0x50, + .flags = I2C_M_RD, + .len = 0, + .buf = NULL, + }; + int rc; + + if (disable_ir) { + dprintk("IR has been disabled, not probing for i2c remote\n"); + return; + } + + memset(&info, 0, sizeof(struct i2c_board_info)); + memset(&dev->init_data, 0, sizeof(dev->init_data)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + + switch (dev->board) { + case SAA7134_BOARD_PINNACLE_PCTV_110i: + case SAA7134_BOARD_PINNACLE_PCTV_310i: + dev->init_data.name = "Pinnacle PCTV"; + if (pinnacle_remote == 0) { + dev->init_data.get_key = get_key_pinnacle_color; + dev->init_data.ir_codes = RC_MAP_PINNACLE_COLOR; + info.addr = 0x47; + } else { + dev->init_data.get_key = get_key_pinnacle_grey; + dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; + info.addr = 0x47; + } + break; + case SAA7134_BOARD_UPMOST_PURPLE_TV: + dev->init_data.name = "Purple TV"; + dev->init_data.get_key = get_key_purpletv; + dev->init_data.ir_codes = RC_MAP_PURPLETV; + info.addr = 0x7a; + break; + case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: + dev->init_data.name = "MSI TV@nywhere Plus"; + dev->init_data.get_key = get_key_msi_tvanywhere_plus; + dev->init_data.ir_codes = RC_MAP_MSI_TVANYWHERE_PLUS; + /* + * MSI TV@nyware Plus requires more frequent polling + * otherwise it will miss some keypresses + */ + dev->init_data.polling_interval = 50; + info.addr = 0x30; + /* MSI TV@nywhere Plus controller doesn't seem to + respond to probes unless we read something from + an existing device. Weird... + REVISIT: might no longer be needed */ + rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1); + dprintk("probe 0x%02x @ %s: %s\n", + msg_msi.addr, dev->i2c_adap.name, + (1 == rc) ? "yes" : "no"); + break; + case SAA7134_BOARD_KWORLD_PC150U: + /* copied and modified from MSI TV@nywhere Plus */ + dev->init_data.name = "Kworld PC150-U"; + dev->init_data.get_key = get_key_kworld_pc150u; + dev->init_data.ir_codes = RC_MAP_KWORLD_PC150U; + info.addr = 0x30; + /* MSI TV@nywhere Plus controller doesn't seem to + respond to probes unless we read something from + an existing device. Weird... + REVISIT: might no longer be needed */ + rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1); + dprintk("probe 0x%02x @ %s: %s\n", + msg_msi.addr, dev->i2c_adap.name, + (1 == rc) ? "yes" : "no"); + break; + case SAA7134_BOARD_HAUPPAUGE_HVR1110: + dev->init_data.name = "HVR 1110"; + dev->init_data.get_key = get_key_hvr1110; + dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; + info.addr = 0x71; + break; + case SAA7134_BOARD_BEHOLD_607FM_MK3: + case SAA7134_BOARD_BEHOLD_607FM_MK5: + case SAA7134_BOARD_BEHOLD_609FM_MK3: + case SAA7134_BOARD_BEHOLD_609FM_MK5: + case SAA7134_BOARD_BEHOLD_607RDS_MK3: + case SAA7134_BOARD_BEHOLD_607RDS_MK5: + case SAA7134_BOARD_BEHOLD_609RDS_MK3: + case SAA7134_BOARD_BEHOLD_609RDS_MK5: + case SAA7134_BOARD_BEHOLD_M6: + case SAA7134_BOARD_BEHOLD_M63: + case SAA7134_BOARD_BEHOLD_M6_EXTRA: + case SAA7134_BOARD_BEHOLD_H6: + case SAA7134_BOARD_BEHOLD_X7: + case SAA7134_BOARD_BEHOLD_H7: + case SAA7134_BOARD_BEHOLD_A7: + dev->init_data.name = "BeholdTV"; + dev->init_data.get_key = get_key_beholdm6xx; + dev->init_data.ir_codes = RC_MAP_BEHOLD; + dev->init_data.type = RC_TYPE_NEC; + info.addr = 0x2d; + break; + case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + info.addr = 0x40; + break; + case SAA7134_BOARD_FLYDVB_TRIO: + dev->init_data.name = "FlyDVB Trio"; + dev->init_data.get_key = get_key_flydvb_trio; + dev->init_data.ir_codes = RC_MAP_FLYDVB; + info.addr = 0x0b; + break; + default: + dprintk("No I2C IR support for board %x\n", dev->board); + return; + } + + if (dev->init_data.name) + info.platform_data = &dev->init_data; + i2c_new_device(&dev->i2c_adap, &info); +} + +static int saa7134_raw_decode_irq(struct saa7134_dev *dev) +{ + struct saa7134_card_ir *ir = dev->remote; + unsigned long timeout; + int space; + + /* Generate initial event */ + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + space = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2) & ir->mask_keydown; + ir_raw_event_store_edge(dev->remote->dev, space ? IR_SPACE : IR_PULSE); + + /* + * Wait 15 ms from the start of the first IR event before processing + * the event. This time is enough for NEC protocol. May need adjustments + * to work with other protocols. + */ + smp_mb(); + + if (!timer_pending(&ir->timer)) { + timeout = jiffies + msecs_to_jiffies(15); + mod_timer(&ir->timer, timeout); + } + + return 1; +} diff --git a/drivers/media/pci/saa7134/saa7134-reg.h b/drivers/media/pci/saa7134/saa7134-reg.h new file mode 100644 index 000000000000..e7e0af101fa7 --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-reg.h @@ -0,0 +1,378 @@ +/* + * + * philips saa7134 registers + */ + +/* ------------------------------------------------------------------ */ +/* + * PCI ID's + */ +#ifndef PCI_DEVICE_ID_PHILIPS_SAA7130 +# define PCI_DEVICE_ID_PHILIPS_SAA7130 0x7130 +#endif +#ifndef PCI_DEVICE_ID_PHILIPS_SAA7133 +# define PCI_DEVICE_ID_PHILIPS_SAA7133 0x7133 +#endif +#ifndef PCI_DEVICE_ID_PHILIPS_SAA7134 +# define PCI_DEVICE_ID_PHILIPS_SAA7134 0x7134 +#endif +#ifndef PCI_DEVICE_ID_PHILIPS_SAA7135 +# define PCI_DEVICE_ID_PHILIPS_SAA7135 0x7135 +#endif + +/* ------------------------------------------------------------------ */ +/* + * registers -- 32 bit + */ + +/* DMA channels, n = 0 ... 6 */ +#define SAA7134_RS_BA1(n) ((0x200 >> 2) + 4*n) +#define SAA7134_RS_BA2(n) ((0x204 >> 2) + 4*n) +#define SAA7134_RS_PITCH(n) ((0x208 >> 2) + 4*n) +#define SAA7134_RS_CONTROL(n) ((0x20c >> 2) + 4*n) +#define SAA7134_RS_CONTROL_WSWAP (0x01 << 25) +#define SAA7134_RS_CONTROL_BSWAP (0x01 << 24) +#define SAA7134_RS_CONTROL_BURST_2 (0x01 << 21) +#define SAA7134_RS_CONTROL_BURST_4 (0x02 << 21) +#define SAA7134_RS_CONTROL_BURST_8 (0x03 << 21) +#define SAA7134_RS_CONTROL_BURST_16 (0x04 << 21) +#define SAA7134_RS_CONTROL_BURST_32 (0x05 << 21) +#define SAA7134_RS_CONTROL_BURST_64 (0x06 << 21) +#define SAA7134_RS_CONTROL_BURST_MAX (0x07 << 21) +#define SAA7134_RS_CONTROL_ME (0x01 << 20) +#define SAA7134_FIFO_SIZE (0x2a0 >> 2) +#define SAA7134_THRESHOULD (0x2a4 >> 2) + +#define SAA7133_NUM_SAMPLES (0x588 >> 2) +#define SAA7133_AUDIO_CHANNEL (0x58c >> 2) +#define SAA7133_AUDIO_FORMAT (0x58f >> 2) +#define SAA7133_DIGITAL_OUTPUT_SEL1 (0x46c >> 2) +#define SAA7133_DIGITAL_OUTPUT_SEL2 (0x470 >> 2) +#define SAA7133_DIGITAL_INPUT_XBAR1 (0x464 >> 2) +#define SAA7133_ANALOG_IO_SELECT (0x594 >> 2) + +/* main control */ +#define SAA7134_MAIN_CTRL (0x2a8 >> 2) +#define SAA7134_MAIN_CTRL_VPLLE (1 << 15) +#define SAA7134_MAIN_CTRL_APLLE (1 << 14) +#define SAA7134_MAIN_CTRL_EXOSC (1 << 13) +#define SAA7134_MAIN_CTRL_EVFE1 (1 << 12) +#define SAA7134_MAIN_CTRL_EVFE2 (1 << 11) +#define SAA7134_MAIN_CTRL_ESFE (1 << 10) +#define SAA7134_MAIN_CTRL_EBADC (1 << 9) +#define SAA7134_MAIN_CTRL_EBDAC (1 << 8) +#define SAA7134_MAIN_CTRL_TE6 (1 << 6) +#define SAA7134_MAIN_CTRL_TE5 (1 << 5) +#define SAA7134_MAIN_CTRL_TE4 (1 << 4) +#define SAA7134_MAIN_CTRL_TE3 (1 << 3) +#define SAA7134_MAIN_CTRL_TE2 (1 << 2) +#define SAA7134_MAIN_CTRL_TE1 (1 << 1) +#define SAA7134_MAIN_CTRL_TE0 (1 << 0) + +/* DMA status */ +#define SAA7134_DMA_STATUS (0x2ac >> 2) + +/* audio / video status */ +#define SAA7134_AV_STATUS (0x2c0 >> 2) +#define SAA7134_AV_STATUS_STEREO (1 << 17) +#define SAA7134_AV_STATUS_DUAL (1 << 16) +#define SAA7134_AV_STATUS_PILOT (1 << 15) +#define SAA7134_AV_STATUS_SMB (1 << 14) +#define SAA7134_AV_STATUS_DMB (1 << 13) +#define SAA7134_AV_STATUS_VDSP (1 << 12) +#define SAA7134_AV_STATUS_IIC_STATUS (3 << 10) +#define SAA7134_AV_STATUS_MVM (7 << 7) +#define SAA7134_AV_STATUS_FIDT (1 << 6) +#define SAA7134_AV_STATUS_INTL (1 << 5) +#define SAA7134_AV_STATUS_RDCAP (1 << 4) +#define SAA7134_AV_STATUS_PWR_ON (1 << 3) +#define SAA7134_AV_STATUS_LOAD_ERR (1 << 2) +#define SAA7134_AV_STATUS_TRIG_ERR (1 << 1) +#define SAA7134_AV_STATUS_CONF_ERR (1 << 0) + +/* interrupt */ +#define SAA7134_IRQ1 (0x2c4 >> 2) +#define SAA7134_IRQ1_INTE_RA3_1 (1 << 25) +#define SAA7134_IRQ1_INTE_RA3_0 (1 << 24) +#define SAA7134_IRQ1_INTE_RA2_3 (1 << 19) +#define SAA7134_IRQ1_INTE_RA2_2 (1 << 18) +#define SAA7134_IRQ1_INTE_RA2_1 (1 << 17) +#define SAA7134_IRQ1_INTE_RA2_0 (1 << 16) +#define SAA7134_IRQ1_INTE_RA1_3 (1 << 11) +#define SAA7134_IRQ1_INTE_RA1_2 (1 << 10) +#define SAA7134_IRQ1_INTE_RA1_1 (1 << 9) +#define SAA7134_IRQ1_INTE_RA1_0 (1 << 8) +#define SAA7134_IRQ1_INTE_RA0_7 (1 << 7) +#define SAA7134_IRQ1_INTE_RA0_6 (1 << 6) +#define SAA7134_IRQ1_INTE_RA0_5 (1 << 5) +#define SAA7134_IRQ1_INTE_RA0_4 (1 << 4) +#define SAA7134_IRQ1_INTE_RA0_3 (1 << 3) +#define SAA7134_IRQ1_INTE_RA0_2 (1 << 2) +#define SAA7134_IRQ1_INTE_RA0_1 (1 << 1) +#define SAA7134_IRQ1_INTE_RA0_0 (1 << 0) + +#define SAA7134_IRQ2 (0x2c8 >> 2) +#define SAA7134_IRQ2_INTE_GPIO23_N (1 << 17) /* negative edge */ +#define SAA7134_IRQ2_INTE_GPIO23_P (1 << 16) /* positive edge */ +#define SAA7134_IRQ2_INTE_GPIO22_N (1 << 15) /* negative edge */ +#define SAA7134_IRQ2_INTE_GPIO22_P (1 << 14) /* positive edge */ +#define SAA7134_IRQ2_INTE_GPIO18_N (1 << 13) /* negative edge */ +#define SAA7134_IRQ2_INTE_GPIO18_P (1 << 12) /* positive edge */ +#define SAA7134_IRQ2_INTE_GPIO16_N (1 << 11) /* negative edge */ +#define SAA7134_IRQ2_INTE_GPIO16_P (1 << 10) /* positive edge */ +#define SAA7134_IRQ2_INTE_SC2 (1 << 9) +#define SAA7134_IRQ2_INTE_SC1 (1 << 8) +#define SAA7134_IRQ2_INTE_SC0 (1 << 7) +#define SAA7134_IRQ2_INTE_DEC4 (1 << 6) +#define SAA7134_IRQ2_INTE_DEC3 (1 << 5) +#define SAA7134_IRQ2_INTE_DEC2 (1 << 4) +#define SAA7134_IRQ2_INTE_DEC1 (1 << 3) +#define SAA7134_IRQ2_INTE_DEC0 (1 << 2) +#define SAA7134_IRQ2_INTE_PE (1 << 1) +#define SAA7134_IRQ2_INTE_AR (1 << 0) + +#define SAA7134_IRQ_REPORT (0x2cc >> 2) +#define SAA7134_IRQ_REPORT_GPIO23 (1 << 17) +#define SAA7134_IRQ_REPORT_GPIO22 (1 << 16) +#define SAA7134_IRQ_REPORT_GPIO18 (1 << 15) +#define SAA7134_IRQ_REPORT_GPIO16 (1 << 14) +#define SAA7134_IRQ_REPORT_LOAD_ERR (1 << 13) +#define SAA7134_IRQ_REPORT_CONF_ERR (1 << 12) +#define SAA7134_IRQ_REPORT_TRIG_ERR (1 << 11) +#define SAA7134_IRQ_REPORT_MMC (1 << 10) +#define SAA7134_IRQ_REPORT_FIDT (1 << 9) +#define SAA7134_IRQ_REPORT_INTL (1 << 8) +#define SAA7134_IRQ_REPORT_RDCAP (1 << 7) +#define SAA7134_IRQ_REPORT_PWR_ON (1 << 6) +#define SAA7134_IRQ_REPORT_PE (1 << 5) +#define SAA7134_IRQ_REPORT_AR (1 << 4) +#define SAA7134_IRQ_REPORT_DONE_RA3 (1 << 3) +#define SAA7134_IRQ_REPORT_DONE_RA2 (1 << 2) +#define SAA7134_IRQ_REPORT_DONE_RA1 (1 << 1) +#define SAA7134_IRQ_REPORT_DONE_RA0 (1 << 0) +#define SAA7134_IRQ_STATUS (0x2d0 >> 2) + + +/* ------------------------------------------------------------------ */ +/* + * registers -- 8 bit + */ + +/* video decoder */ +#define SAA7134_INCR_DELAY 0x101 +#define SAA7134_ANALOG_IN_CTRL1 0x102 +#define SAA7134_ANALOG_IN_CTRL2 0x103 +#define SAA7134_ANALOG_IN_CTRL3 0x104 +#define SAA7134_ANALOG_IN_CTRL4 0x105 +#define SAA7134_HSYNC_START 0x106 +#define SAA7134_HSYNC_STOP 0x107 +#define SAA7134_SYNC_CTRL 0x108 +#define SAA7134_LUMA_CTRL 0x109 +#define SAA7134_DEC_LUMA_BRIGHT 0x10a +#define SAA7134_DEC_LUMA_CONTRAST 0x10b +#define SAA7134_DEC_CHROMA_SATURATION 0x10c +#define SAA7134_DEC_CHROMA_HUE 0x10d +#define SAA7134_CHROMA_CTRL1 0x10e +#define SAA7134_CHROMA_GAIN 0x10f +#define SAA7134_CHROMA_CTRL2 0x110 +#define SAA7134_MODE_DELAY_CTRL 0x111 + +#define SAA7134_ANALOG_ADC 0x114 +#define SAA7134_VGATE_START 0x115 +#define SAA7134_VGATE_STOP 0x116 +#define SAA7134_MISC_VGATE_MSB 0x117 +#define SAA7134_RAW_DATA_GAIN 0x118 +#define SAA7134_RAW_DATA_OFFSET 0x119 +#define SAA7134_STATUS_VIDEO1 0x11e +#define SAA7134_STATUS_VIDEO2 0x11f + +/* video scaler */ +#define SAA7134_SOURCE_TIMING1 0x000 +#define SAA7134_SOURCE_TIMING2 0x001 +#define SAA7134_REGION_ENABLE 0x004 +#define SAA7134_SCALER_STATUS0 0x006 +#define SAA7134_SCALER_STATUS1 0x007 +#define SAA7134_START_GREEN 0x00c +#define SAA7134_START_BLUE 0x00d +#define SAA7134_START_RED 0x00e +#define SAA7134_GREEN_PATH(x) (0x010 +x) +#define SAA7134_BLUE_PATH(x) (0x020 +x) +#define SAA7134_RED_PATH(x) (0x030 +x) + +#define TASK_A 0x040 +#define TASK_B 0x080 +#define SAA7134_TASK_CONDITIONS(t) (0x000 +t) +#define SAA7134_FIELD_HANDLING(t) (0x001 +t) +#define SAA7134_DATA_PATH(t) (0x002 +t) +#define SAA7134_VBI_H_START1(t) (0x004 +t) +#define SAA7134_VBI_H_START2(t) (0x005 +t) +#define SAA7134_VBI_H_STOP1(t) (0x006 +t) +#define SAA7134_VBI_H_STOP2(t) (0x007 +t) +#define SAA7134_VBI_V_START1(t) (0x008 +t) +#define SAA7134_VBI_V_START2(t) (0x009 +t) +#define SAA7134_VBI_V_STOP1(t) (0x00a +t) +#define SAA7134_VBI_V_STOP2(t) (0x00b +t) +#define SAA7134_VBI_H_LEN1(t) (0x00c +t) +#define SAA7134_VBI_H_LEN2(t) (0x00d +t) +#define SAA7134_VBI_V_LEN1(t) (0x00e +t) +#define SAA7134_VBI_V_LEN2(t) (0x00f +t) + +#define SAA7134_VIDEO_H_START1(t) (0x014 +t) +#define SAA7134_VIDEO_H_START2(t) (0x015 +t) +#define SAA7134_VIDEO_H_STOP1(t) (0x016 +t) +#define SAA7134_VIDEO_H_STOP2(t) (0x017 +t) +#define SAA7134_VIDEO_V_START1(t) (0x018 +t) +#define SAA7134_VIDEO_V_START2(t) (0x019 +t) +#define SAA7134_VIDEO_V_STOP1(t) (0x01a +t) +#define SAA7134_VIDEO_V_STOP2(t) (0x01b +t) +#define SAA7134_VIDEO_PIXELS1(t) (0x01c +t) +#define SAA7134_VIDEO_PIXELS2(t) (0x01d +t) +#define SAA7134_VIDEO_LINES1(t) (0x01e +t) +#define SAA7134_VIDEO_LINES2(t) (0x01f +t) + +#define SAA7134_H_PRESCALE(t) (0x020 +t) +#define SAA7134_ACC_LENGTH(t) (0x021 +t) +#define SAA7134_LEVEL_CTRL(t) (0x022 +t) +#define SAA7134_FIR_PREFILTER_CTRL(t) (0x023 +t) +#define SAA7134_LUMA_BRIGHT(t) (0x024 +t) +#define SAA7134_LUMA_CONTRAST(t) (0x025 +t) +#define SAA7134_CHROMA_SATURATION(t) (0x026 +t) +#define SAA7134_VBI_H_SCALE_INC1(t) (0x028 +t) +#define SAA7134_VBI_H_SCALE_INC2(t) (0x029 +t) +#define SAA7134_VBI_PHASE_OFFSET_LUMA(t) (0x02a +t) +#define SAA7134_VBI_PHASE_OFFSET_CHROMA(t) (0x02b +t) +#define SAA7134_H_SCALE_INC1(t) (0x02c +t) +#define SAA7134_H_SCALE_INC2(t) (0x02d +t) +#define SAA7134_H_PHASE_OFF_LUMA(t) (0x02e +t) +#define SAA7134_H_PHASE_OFF_CHROMA(t) (0x02f +t) +#define SAA7134_V_SCALE_RATIO1(t) (0x030 +t) +#define SAA7134_V_SCALE_RATIO2(t) (0x031 +t) +#define SAA7134_V_FILTER(t) (0x032 +t) +#define SAA7134_V_PHASE_OFFSET0(t) (0x034 +t) +#define SAA7134_V_PHASE_OFFSET1(t) (0x035 +t) +#define SAA7134_V_PHASE_OFFSET2(t) (0x036 +t) +#define SAA7134_V_PHASE_OFFSET3(t) (0x037 +t) + +/* clipping & dma */ +#define SAA7134_OFMT_VIDEO_A 0x300 +#define SAA7134_OFMT_DATA_A 0x301 +#define SAA7134_OFMT_VIDEO_B 0x302 +#define SAA7134_OFMT_DATA_B 0x303 +#define SAA7134_ALPHA_NOCLIP 0x304 +#define SAA7134_ALPHA_CLIP 0x305 +#define SAA7134_UV_PIXEL 0x308 +#define SAA7134_CLIP_RED 0x309 +#define SAA7134_CLIP_GREEN 0x30a +#define SAA7134_CLIP_BLUE 0x30b + +/* i2c bus */ +#define SAA7134_I2C_ATTR_STATUS 0x180 +#define SAA7134_I2C_DATA 0x181 +#define SAA7134_I2C_CLOCK_SELECT 0x182 +#define SAA7134_I2C_TIMER 0x183 + +/* audio */ +#define SAA7134_NICAM_ADD_DATA1 0x140 +#define SAA7134_NICAM_ADD_DATA2 0x141 +#define SAA7134_NICAM_STATUS 0x142 +#define SAA7134_AUDIO_STATUS 0x143 +#define SAA7134_NICAM_ERROR_COUNT 0x144 +#define SAA7134_IDENT_SIF 0x145 +#define SAA7134_LEVEL_READOUT1 0x146 +#define SAA7134_LEVEL_READOUT2 0x147 +#define SAA7134_NICAM_ERROR_LOW 0x148 +#define SAA7134_NICAM_ERROR_HIGH 0x149 +#define SAA7134_DCXO_IDENT_CTRL 0x14a +#define SAA7134_DEMODULATOR 0x14b +#define SAA7134_AGC_GAIN_SELECT 0x14c +#define SAA7134_CARRIER1_FREQ0 0x150 +#define SAA7134_CARRIER1_FREQ1 0x151 +#define SAA7134_CARRIER1_FREQ2 0x152 +#define SAA7134_CARRIER2_FREQ0 0x154 +#define SAA7134_CARRIER2_FREQ1 0x155 +#define SAA7134_CARRIER2_FREQ2 0x156 +#define SAA7134_NUM_SAMPLES0 0x158 +#define SAA7134_NUM_SAMPLES1 0x159 +#define SAA7134_NUM_SAMPLES2 0x15a +#define SAA7134_AUDIO_FORMAT_CTRL 0x15b +#define SAA7134_MONITOR_SELECT 0x160 +#define SAA7134_FM_DEEMPHASIS 0x161 +#define SAA7134_FM_DEMATRIX 0x162 +#define SAA7134_CHANNEL1_LEVEL 0x163 +#define SAA7134_CHANNEL2_LEVEL 0x164 +#define SAA7134_NICAM_CONFIG 0x165 +#define SAA7134_NICAM_LEVEL_ADJUST 0x166 +#define SAA7134_STEREO_DAC_OUTPUT_SELECT 0x167 +#define SAA7134_I2S_OUTPUT_FORMAT 0x168 +#define SAA7134_I2S_OUTPUT_SELECT 0x169 +#define SAA7134_I2S_OUTPUT_LEVEL 0x16a +#define SAA7134_DSP_OUTPUT_SELECT 0x16b +#define SAA7134_AUDIO_MUTE_CTRL 0x16c +#define SAA7134_SIF_SAMPLE_FREQ 0x16d +#define SAA7134_ANALOG_IO_SELECT 0x16e +#define SAA7134_AUDIO_CLOCK0 0x170 +#define SAA7134_AUDIO_CLOCK1 0x171 +#define SAA7134_AUDIO_CLOCK2 0x172 +#define SAA7134_AUDIO_PLL_CTRL 0x173 +#define SAA7134_AUDIO_CLOCKS_PER_FIELD0 0x174 +#define SAA7134_AUDIO_CLOCKS_PER_FIELD1 0x175 +#define SAA7134_AUDIO_CLOCKS_PER_FIELD2 0x176 + +/* video port output */ +#define SAA7134_VIDEO_PORT_CTRL0 0x190 +#define SAA7134_VIDEO_PORT_CTRL1 0x191 +#define SAA7134_VIDEO_PORT_CTRL2 0x192 +#define SAA7134_VIDEO_PORT_CTRL3 0x193 +#define SAA7134_VIDEO_PORT_CTRL4 0x194 +#define SAA7134_VIDEO_PORT_CTRL5 0x195 +#define SAA7134_VIDEO_PORT_CTRL6 0x196 +#define SAA7134_VIDEO_PORT_CTRL7 0x197 +#define SAA7134_VIDEO_PORT_CTRL8 0x198 + +/* transport stream interface */ +#define SAA7134_TS_PARALLEL 0x1a0 +#define SAA7134_TS_PARALLEL_SERIAL 0x1a1 +#define SAA7134_TS_SERIAL0 0x1a2 +#define SAA7134_TS_SERIAL1 0x1a3 +#define SAA7134_TS_DMA0 0x1a4 +#define SAA7134_TS_DMA1 0x1a5 +#define SAA7134_TS_DMA2 0x1a6 + +/* GPIO Controls */ +#define SAA7134_GPIO_GPRESCAN 0x80 +#define SAA7134_GPIO_27_25 0x0E + +#define SAA7134_GPIO_GPMODE0 0x1B0 +#define SAA7134_GPIO_GPMODE1 0x1B1 +#define SAA7134_GPIO_GPMODE2 0x1B2 +#define SAA7134_GPIO_GPMODE3 0x1B3 +#define SAA7134_GPIO_GPSTATUS0 0x1B4 +#define SAA7134_GPIO_GPSTATUS1 0x1B5 +#define SAA7134_GPIO_GPSTATUS2 0x1B6 +#define SAA7134_GPIO_GPSTATUS3 0x1B7 + +/* I2S output */ +#define SAA7134_I2S_AUDIO_OUTPUT 0x1c0 + +/* test modes */ +#define SAA7134_SPECIAL_MODE 0x1d0 +#define SAA7134_PRODUCTION_TEST_MODE 0x1d1 + +/* audio -- saa7133 + saa7135 only */ +#define SAA7135_DSP_RWSTATE 0x580 +#define SAA7135_DSP_RWSTATE_ERR (1 << 3) +#define SAA7135_DSP_RWSTATE_IDA (1 << 2) +#define SAA7135_DSP_RWSTATE_RDB (1 << 1) +#define SAA7135_DSP_RWSTATE_WRR (1 << 0) + +#define SAA7135_DSP_RWCLEAR 0x586 +#define SAA7135_DSP_RWCLEAR_RERR 1 + +#define SAA7133_I2S_AUDIO_CONTROL 0x591 +/* ------------------------------------------------------------------ */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff --git a/drivers/media/pci/saa7134/saa7134-ts.c b/drivers/media/pci/saa7134/saa7134-ts.c new file mode 100644 index 000000000000..2e3f4b412d8c --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-ts.c @@ -0,0 +1,327 @@ +/* + * + * device driver for philips saa7134 based TV cards + * video4linux video interface + * + * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> + +#include "saa7134-reg.h" +#include "saa7134.h" + +/* ------------------------------------------------------------------ */ + +static unsigned int ts_debug; +module_param(ts_debug, int, 0644); +MODULE_PARM_DESC(ts_debug,"enable debug messages [ts]"); + +#define dprintk(fmt, arg...) if (ts_debug) \ + printk(KERN_DEBUG "%s/ts: " fmt, dev->name , ## arg) + +/* ------------------------------------------------------------------ */ + +static int buffer_activate(struct saa7134_dev *dev, + struct saa7134_buf *buf, + struct saa7134_buf *next) +{ + + dprintk("buffer_activate [%p]",buf); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->top_seen = 0; + + if (NULL == next) + next = buf; + if (V4L2_FIELD_TOP == buf->vb.field) { + dprintk("- [top] buf=%p next=%p\n",buf,next); + saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(buf)); + saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(next)); + } else { + dprintk("- [bottom] buf=%p next=%p\n",buf,next); + saa_writel(SAA7134_RS_BA1(5),saa7134_buffer_base(next)); + saa_writel(SAA7134_RS_BA2(5),saa7134_buffer_base(buf)); + } + + /* start DMA */ + saa7134_set_dmabits(dev); + + mod_timer(&dev->ts_q.timeout, jiffies+TS_BUFFER_TIMEOUT); + + if (!dev->ts_started) + saa7134_ts_start(dev); + + return 0; +} + +static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct saa7134_dev *dev = q->priv_data; + struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + unsigned int lines, llength, size; + int err; + + dprintk("buffer_prepare [%p,%s]\n",buf,v4l2_field_names[field]); + + llength = TS_PACKET_SIZE; + lines = dev->ts.nr_packets; + + size = lines * llength; + if (0 != buf->vb.baddr && buf->vb.bsize < size) + return -EINVAL; + + if (buf->vb.size != size) { + saa7134_dma_free(q,buf); + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + + struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + + dprintk("buffer_prepare: needs_init\n"); + + buf->vb.width = llength; + buf->vb.height = lines; + buf->vb.size = size; + buf->pt = &dev->ts.pt_ts; + + err = videobuf_iolock(q,&buf->vb,NULL); + if (err) + goto oops; + err = saa7134_pgtable_build(dev->pci,buf->pt, + dma->sglist, + dma->sglen, + saa7134_buffer_startpage(buf)); + if (err) + goto oops; + } + + buf->vb.state = VIDEOBUF_PREPARED; + buf->activate = buffer_activate; + buf->vb.field = field; + return 0; + + oops: + saa7134_dma_free(q,buf); + return err; +} + +static int +buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + struct saa7134_dev *dev = q->priv_data; + + *size = TS_PACKET_SIZE * dev->ts.nr_packets; + if (0 == *count) + *count = dev->ts.nr_bufs; + *count = saa7134_buffer_count(*size,*count); + + return 0; +} + +static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct saa7134_dev *dev = q->priv_data; + struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + + saa7134_buffer_queue(dev,&dev->ts_q,buf); +} + +static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + struct saa7134_dev *dev = q->priv_data; + + if (dev->ts_started) + saa7134_ts_stop(dev); + + saa7134_dma_free(q,buf); +} + +struct videobuf_queue_ops saa7134_ts_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; +EXPORT_SYMBOL_GPL(saa7134_ts_qops); + +/* ----------------------------------------------------------- */ +/* exported stuff */ + +static unsigned int tsbufs = 8; +module_param(tsbufs, int, 0444); +MODULE_PARM_DESC(tsbufs, "number of ts buffers for read/write IO, range 2-32"); + +static unsigned int ts_nr_packets = 64; +module_param(ts_nr_packets, int, 0444); +MODULE_PARM_DESC(ts_nr_packets,"size of a ts buffers (in ts packets)"); + +int saa7134_ts_init_hw(struct saa7134_dev *dev) +{ + /* deactivate TS softreset */ + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + /* TSSOP high active, TSVAL high active, TSLOCK ignored */ + saa_writeb(SAA7134_TS_PARALLEL, 0x6c); + saa_writeb(SAA7134_TS_PARALLEL_SERIAL, (TS_PACKET_SIZE-1)); + saa_writeb(SAA7134_TS_DMA0, ((dev->ts.nr_packets-1)&0xff)); + saa_writeb(SAA7134_TS_DMA1, (((dev->ts.nr_packets-1)>>8)&0xff)); + /* TSNOPIT=0, TSCOLAP=0 */ + saa_writeb(SAA7134_TS_DMA2, + ((((dev->ts.nr_packets-1)>>16)&0x3f) | 0x00)); + + return 0; +} + +int saa7134_ts_init1(struct saa7134_dev *dev) +{ + /* sanitycheck insmod options */ + if (tsbufs < 2) + tsbufs = 2; + if (tsbufs > VIDEO_MAX_FRAME) + tsbufs = VIDEO_MAX_FRAME; + if (ts_nr_packets < 4) + ts_nr_packets = 4; + if (ts_nr_packets > 312) + ts_nr_packets = 312; + dev->ts.nr_bufs = tsbufs; + dev->ts.nr_packets = ts_nr_packets; + + INIT_LIST_HEAD(&dev->ts_q.queue); + init_timer(&dev->ts_q.timeout); + dev->ts_q.timeout.function = saa7134_buffer_timeout; + dev->ts_q.timeout.data = (unsigned long)(&dev->ts_q); + dev->ts_q.dev = dev; + dev->ts_q.need_two = 1; + dev->ts_started = 0; + saa7134_pgtable_alloc(dev->pci,&dev->ts.pt_ts); + + /* init TS hw */ + saa7134_ts_init_hw(dev); + + return 0; +} + +/* Function for stop TS */ +int saa7134_ts_stop(struct saa7134_dev *dev) +{ + dprintk("TS stop\n"); + + BUG_ON(!dev->ts_started); + + /* Stop TS stream */ + switch (saa7134_boards[dev->board].ts_type) { + case SAA7134_MPEG_TS_PARALLEL: + saa_writeb(SAA7134_TS_PARALLEL, 0x6c); + dev->ts_started = 0; + break; + case SAA7134_MPEG_TS_SERIAL: + saa_writeb(SAA7134_TS_SERIAL0, 0x40); + dev->ts_started = 0; + break; + } + return 0; +} + +/* Function for start TS */ +int saa7134_ts_start(struct saa7134_dev *dev) +{ + dprintk("TS start\n"); + + BUG_ON(dev->ts_started); + + /* dma: setup channel 5 (= TS) */ + saa_writeb(SAA7134_TS_DMA0, (dev->ts.nr_packets - 1) & 0xff); + saa_writeb(SAA7134_TS_DMA1, + ((dev->ts.nr_packets - 1) >> 8) & 0xff); + /* TSNOPIT=0, TSCOLAP=0 */ + saa_writeb(SAA7134_TS_DMA2, + (((dev->ts.nr_packets - 1) >> 16) & 0x3f) | 0x00); + saa_writel(SAA7134_RS_PITCH(5), TS_PACKET_SIZE); + saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_16 | + SAA7134_RS_CONTROL_ME | + (dev->ts.pt_ts.dma >> 12)); + + /* reset hardware TS buffers */ + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + saa_writeb(SAA7134_TS_SERIAL1, 0x03); + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + saa_writeb(SAA7134_TS_SERIAL1, 0x01); + + /* TS clock non-inverted */ + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + + /* Start TS stream */ + switch (saa7134_boards[dev->board].ts_type) { + case SAA7134_MPEG_TS_PARALLEL: + saa_writeb(SAA7134_TS_SERIAL0, 0x40); + saa_writeb(SAA7134_TS_PARALLEL, 0xec | + (saa7134_boards[dev->board].ts_force_val << 4)); + break; + case SAA7134_MPEG_TS_SERIAL: + saa_writeb(SAA7134_TS_SERIAL0, 0xd8); + saa_writeb(SAA7134_TS_PARALLEL, 0x6c | + (saa7134_boards[dev->board].ts_force_val << 4)); + saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 0xbc); + saa_writeb(SAA7134_TS_SERIAL1, 0x02); + break; + } + + dev->ts_started = 1; + + return 0; +} + +int saa7134_ts_fini(struct saa7134_dev *dev) +{ + saa7134_pgtable_free(dev->pci,&dev->ts.pt_ts); + return 0; +} + +void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status) +{ + enum v4l2_field field; + + spin_lock(&dev->slock); + if (dev->ts_q.curr) { + field = dev->ts_q.curr->vb.field; + if (field == V4L2_FIELD_TOP) { + if ((status & 0x100000) != 0x000000) + goto done; + } else { + if ((status & 0x100000) != 0x100000) + goto done; + } + saa7134_buffer_finish(dev,&dev->ts_q,VIDEOBUF_DONE); + } + saa7134_buffer_next(dev,&dev->ts_q); + + done: + spin_unlock(&dev->slock); +} + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/pci/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c new file mode 100644 index 000000000000..b7a99bee2f98 --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-tvaudio.c @@ -0,0 +1,1087 @@ +/* + * + * device driver for philips saa7134 based TV cards + * tv audio decoder (fm stereo, nicam, ...) + * + * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/delay.h> +#include <linux/freezer.h> +#include <asm/div64.h> + +#include "saa7134-reg.h" +#include "saa7134.h" + +/* ------------------------------------------------------------------ */ + +static unsigned int audio_debug; +module_param(audio_debug, int, 0644); +MODULE_PARM_DESC(audio_debug,"enable debug messages [tv audio]"); + +static unsigned int audio_ddep; +module_param(audio_ddep, int, 0644); +MODULE_PARM_DESC(audio_ddep,"audio ddep overwrite"); + +static int audio_clock_override = UNSET; +module_param(audio_clock_override, int, 0644); + +static int audio_clock_tweak; +module_param(audio_clock_tweak, int, 0644); +MODULE_PARM_DESC(audio_clock_tweak, "Audio clock tick fine tuning for cards with audio crystal that's slightly off (range [-1024 .. 1024])"); + +#define dprintk(fmt, arg...) if (audio_debug) \ + printk(KERN_DEBUG "%s/audio: " fmt, dev->name , ## arg) +#define d2printk(fmt, arg...) if (audio_debug > 1) \ + printk(KERN_DEBUG "%s/audio: " fmt, dev->name, ## arg) + +#define print_regb(reg) printk("%s: reg 0x%03x [%-16s]: 0x%02x\n", \ + dev->name,(SAA7134_##reg),(#reg),saa_readb((SAA7134_##reg))) + +/* msecs */ +#define SCAN_INITIAL_DELAY 1000 +#define SCAN_SAMPLE_DELAY 200 +#define SCAN_SUBCARRIER_DELAY 2000 + +/* ------------------------------------------------------------------ */ +/* saa7134 code */ + +static struct mainscan { + char *name; + v4l2_std_id std; + int carr; +} mainscan[] = { + { + .name = "MN", + .std = V4L2_STD_MN, + .carr = 4500, + },{ + .name = "BGH", + .std = V4L2_STD_B | V4L2_STD_GH, + .carr = 5500, + },{ + .name = "I", + .std = V4L2_STD_PAL_I, + .carr = 6000, + },{ + .name = "DKL", + .std = V4L2_STD_DK | V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC, + .carr = 6500, + } +}; + +static struct saa7134_tvaudio tvaudio[] = { + { + .name = "PAL-B/G FM-stereo", + .std = V4L2_STD_PAL_BG, + .mode = TVAUDIO_FM_BG_STEREO, + .carr1 = 5500, + .carr2 = 5742, + },{ + .name = "PAL-D/K1 FM-stereo", + .std = V4L2_STD_PAL_DK, + .carr1 = 6500, + .carr2 = 6258, + .mode = TVAUDIO_FM_BG_STEREO, + },{ + .name = "PAL-D/K2 FM-stereo", + .std = V4L2_STD_PAL_DK, + .carr1 = 6500, + .carr2 = 6742, + .mode = TVAUDIO_FM_BG_STEREO, + },{ + .name = "PAL-D/K3 FM-stereo", + .std = V4L2_STD_PAL_DK, + .carr1 = 6500, + .carr2 = 5742, + .mode = TVAUDIO_FM_BG_STEREO, + },{ + .name = "PAL-B/G NICAM", + .std = V4L2_STD_PAL_BG, + .carr1 = 5500, + .carr2 = 5850, + .mode = TVAUDIO_NICAM_FM, + },{ + .name = "PAL-I NICAM", + .std = V4L2_STD_PAL_I, + .carr1 = 6000, + .carr2 = 6552, + .mode = TVAUDIO_NICAM_FM, + },{ + .name = "PAL-D/K NICAM", + .std = V4L2_STD_PAL_DK, + .carr1 = 6500, + .carr2 = 5850, + .mode = TVAUDIO_NICAM_FM, + },{ + .name = "SECAM-L NICAM", + .std = V4L2_STD_SECAM_L, + .carr1 = 6500, + .carr2 = 5850, + .mode = TVAUDIO_NICAM_AM, + },{ + .name = "SECAM-D/K NICAM", + .std = V4L2_STD_SECAM_DK, + .carr1 = 6500, + .carr2 = 5850, + .mode = TVAUDIO_NICAM_FM, + },{ + .name = "NTSC-A2 FM-stereo", + .std = V4L2_STD_NTSC, + .carr1 = 4500, + .carr2 = 4724, + .mode = TVAUDIO_FM_K_STEREO, + },{ + .name = "NTSC-M", + .std = V4L2_STD_NTSC, + .carr1 = 4500, + .carr2 = -1, + .mode = TVAUDIO_FM_MONO, + } +}; +#define TVAUDIO ARRAY_SIZE(tvaudio) + +/* ------------------------------------------------------------------ */ + +static u32 tvaudio_carr2reg(u32 carrier) +{ + u64 a = carrier; + + a <<= 24; + do_div(a,12288); + return a; +} + +static void tvaudio_setcarrier(struct saa7134_dev *dev, + int primary, int secondary) +{ + if (-1 == secondary) + secondary = primary; + saa_writel(SAA7134_CARRIER1_FREQ0 >> 2, tvaudio_carr2reg(primary)); + saa_writel(SAA7134_CARRIER2_FREQ0 >> 2, tvaudio_carr2reg(secondary)); +} + +#define SAA7134_MUTE_MASK 0xbb +#define SAA7134_MUTE_ANALOG 0x04 +#define SAA7134_MUTE_I2S 0x40 + +static void mute_input_7134(struct saa7134_dev *dev) +{ + unsigned int mute; + struct saa7134_input *in; + int ausel=0, ics=0, ocs=0; + int mask; + + /* look what is to do ... */ + in = dev->input; + mute = (dev->ctl_mute || + (dev->automute && (&card(dev).radio) != in)); + if (card(dev).mute.name) { + /* + * 7130 - we'll mute using some unconnected audio input + * 7134 - we'll probably should switch external mux with gpio + */ + if (mute) + in = &card(dev).mute; + } + + if (dev->hw_mute == mute && + dev->hw_input == in && !dev->insuspend) { + dprintk("mute/input: nothing to do [mute=%d,input=%s]\n", + mute,in->name); + return; + } + + dprintk("ctl_mute=%d automute=%d input=%s => mute=%d input=%s\n", + dev->ctl_mute,dev->automute,dev->input->name,mute,in->name); + dev->hw_mute = mute; + dev->hw_input = in; + + if (PCI_DEVICE_ID_PHILIPS_SAA7134 == dev->pci->device) + /* 7134 mute */ + saa_writeb(SAA7134_AUDIO_MUTE_CTRL, mute ? + SAA7134_MUTE_MASK | + SAA7134_MUTE_ANALOG | + SAA7134_MUTE_I2S : + SAA7134_MUTE_MASK); + + /* switch internal audio mux */ + switch (in->amux) { + case TV: ausel=0xc0; ics=0x00; ocs=0x02; break; + case LINE1: ausel=0x80; ics=0x00; ocs=0x00; break; + case LINE2: ausel=0x80; ics=0x08; ocs=0x01; break; + case LINE2_LEFT: ausel=0x80; ics=0x08; ocs=0x05; break; + } + saa_andorb(SAA7134_AUDIO_FORMAT_CTRL, 0xc0, ausel); + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x08, ics); + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, ocs); + // for oss, we need to change the clock configuration + if (in->amux == TV) + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x00); + else + saa_andorb(SAA7134_SIF_SAMPLE_FREQ, 0x03, 0x01); + + /* switch gpio-connected external audio mux */ + if (0 == card(dev).gpiomask) + return; + + mask = card(dev).gpiomask; + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, in->gpio); + saa7134_track_gpio(dev,in->name); +} + +static void tvaudio_setmode(struct saa7134_dev *dev, + struct saa7134_tvaudio *audio, + char *note) +{ + int acpf, tweak = 0; + + if (dev->tvnorm->id == V4L2_STD_NTSC) { + acpf = 0x19066; + } else { + acpf = 0x1e000; + } + if (audio_clock_tweak > -1024 && audio_clock_tweak < 1024) + tweak = audio_clock_tweak; + + if (note) + dprintk("tvaudio_setmode: %s %s [%d.%03d/%d.%03d MHz] acpf=%d%+d\n", + note,audio->name, + audio->carr1 / 1000, audio->carr1 % 1000, + audio->carr2 / 1000, audio->carr2 % 1000, + acpf, tweak); + + acpf += tweak; + saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD0, (acpf & 0x0000ff) >> 0); + saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD1, (acpf & 0x00ff00) >> 8); + saa_writeb(SAA7134_AUDIO_CLOCKS_PER_FIELD2, (acpf & 0x030000) >> 16); + tvaudio_setcarrier(dev,audio->carr1,audio->carr2); + + switch (audio->mode) { + case TVAUDIO_FM_MONO: + case TVAUDIO_FM_BG_STEREO: + saa_writeb(SAA7134_DEMODULATOR, 0x00); + saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00); + saa_writeb(SAA7134_FM_DEEMPHASIS, 0x22); + saa_writeb(SAA7134_FM_DEMATRIX, 0x80); + saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa0); + break; + case TVAUDIO_FM_K_STEREO: + saa_writeb(SAA7134_DEMODULATOR, 0x00); + saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x01); + saa_writeb(SAA7134_FM_DEEMPHASIS, 0x22); + saa_writeb(SAA7134_FM_DEMATRIX, 0x80); + saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa0); + break; + case TVAUDIO_NICAM_FM: + saa_writeb(SAA7134_DEMODULATOR, 0x10); + saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00); + saa_writeb(SAA7134_FM_DEEMPHASIS, 0x44); + saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa1); + saa_writeb(SAA7134_NICAM_CONFIG, 0x00); + break; + case TVAUDIO_NICAM_AM: + saa_writeb(SAA7134_DEMODULATOR, 0x12); + saa_writeb(SAA7134_DCXO_IDENT_CTRL, 0x00); + saa_writeb(SAA7134_FM_DEEMPHASIS, 0x44); + saa_writeb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0xa1); + saa_writeb(SAA7134_NICAM_CONFIG, 0x00); + break; + case TVAUDIO_FM_SAT_STEREO: + /* not implemented (yet) */ + break; + } +} + +static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) +{ + if (dev->thread.scan1 == dev->thread.scan2 && + !kthread_should_stop()) { + if (timeout < 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + } else { + schedule_timeout_interruptible + (msecs_to_jiffies(timeout)); + } + } + return dev->thread.scan1 != dev->thread.scan2; +} + +static int tvaudio_checkcarrier(struct saa7134_dev *dev, struct mainscan *scan) +{ + __s32 left,right,value; + + if (!(dev->tvnorm->id & scan->std)) { + value = 0; + dprintk("skipping %d.%03d MHz [%4s]\n", + scan->carr / 1000, scan->carr % 1000, scan->name); + return 0; + } + + if (audio_debug > 1) { + int i; + dprintk("debug %d:",scan->carr); + for (i = -150; i <= 150; i += 30) { + tvaudio_setcarrier(dev,scan->carr+i,scan->carr+i); + saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY)) + return -1; + value = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + if (0 == i) + printk(" # %6d # ",value >> 16); + else + printk(" %6d",value >> 16); + } + printk("\n"); + } + + tvaudio_setcarrier(dev,scan->carr-90,scan->carr-90); + saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY)) + return -1; + left = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + + tvaudio_setcarrier(dev,scan->carr+90,scan->carr+90); + saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + if (tvaudio_sleep(dev,SCAN_SAMPLE_DELAY)) + return -1; + right = saa_readl(SAA7134_LEVEL_READOUT1 >> 2); + + left >>= 16; + right >>= 16; + value = left > right ? left - right : right - left; + dprintk("scanning %d.%03d MHz [%4s] => dc is %5d [%d/%d]\n", + scan->carr / 1000, scan->carr % 1000, + scan->name, value, left, right); + return value; +} + + +static int tvaudio_getstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *audio) +{ + __u32 idp, nicam, nicam_status; + int retval = -1; + + switch (audio->mode) { + case TVAUDIO_FM_MONO: + return V4L2_TUNER_SUB_MONO; + case TVAUDIO_FM_K_STEREO: + case TVAUDIO_FM_BG_STEREO: + idp = (saa_readb(SAA7134_IDENT_SIF) & 0xe0) >> 5; + dprintk("getstereo: fm/stereo: idp=0x%x\n",idp); + if (0x03 == (idp & 0x03)) + retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + else if (0x05 == (idp & 0x05)) + retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + else if (0x01 == (idp & 0x01)) + retval = V4L2_TUNER_SUB_MONO; + break; + case TVAUDIO_FM_SAT_STEREO: + /* not implemented (yet) */ + break; + case TVAUDIO_NICAM_FM: + case TVAUDIO_NICAM_AM: + nicam = saa_readb(SAA7134_AUDIO_STATUS); + dprintk("getstereo: nicam=0x%x\n",nicam); + if (nicam & 0x1) { + nicam_status = saa_readb(SAA7134_NICAM_STATUS); + dprintk("getstereo: nicam_status=0x%x\n", nicam_status); + + switch (nicam_status & 0x03) { + case 0x01: + retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + break; + case 0x02: + retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + break; + default: + retval = V4L2_TUNER_SUB_MONO; + } + } else { + /* No nicam detected */ + } + break; + } + if (retval != -1) + dprintk("found audio subchannels:%s%s%s%s\n", + (retval & V4L2_TUNER_SUB_MONO) ? " mono" : "", + (retval & V4L2_TUNER_SUB_STEREO) ? " stereo" : "", + (retval & V4L2_TUNER_SUB_LANG1) ? " lang1" : "", + (retval & V4L2_TUNER_SUB_LANG2) ? " lang2" : ""); + return retval; +} + +static int tvaudio_setstereo(struct saa7134_dev *dev, struct saa7134_tvaudio *audio, + u32 mode) +{ + static char *name[] = { + [ V4L2_TUNER_MODE_MONO ] = "mono", + [ V4L2_TUNER_MODE_STEREO ] = "stereo", + [ V4L2_TUNER_MODE_LANG1 ] = "lang1", + [ V4L2_TUNER_MODE_LANG2 ] = "lang2", + [ V4L2_TUNER_MODE_LANG1_LANG2 ] = "lang1+lang2", + }; + static u32 fm[] = { + [ V4L2_TUNER_MODE_MONO ] = 0x00, /* ch1 */ + [ V4L2_TUNER_MODE_STEREO ] = 0x80, /* auto */ + [ V4L2_TUNER_MODE_LANG1 ] = 0x00, /* ch1 */ + [ V4L2_TUNER_MODE_LANG2 ] = 0x01, /* ch2 */ + [ V4L2_TUNER_MODE_LANG1_LANG2 ] = 0x80, /* auto */ + }; + u32 reg; + + switch (audio->mode) { + case TVAUDIO_FM_MONO: + /* nothing to do ... */ + break; + case TVAUDIO_FM_K_STEREO: + case TVAUDIO_FM_BG_STEREO: + case TVAUDIO_NICAM_AM: + case TVAUDIO_NICAM_FM: + dprintk("setstereo [fm] => %s\n", + name[ mode % ARRAY_SIZE(name) ]); + reg = fm[ mode % ARRAY_SIZE(fm) ]; + saa_writeb(SAA7134_FM_DEMATRIX, reg); + break; + case TVAUDIO_FM_SAT_STEREO: + /* Not implemented */ + break; + } + return 0; +} + +static int tvaudio_thread(void *data) +{ + struct saa7134_dev *dev = data; + int carr_vals[ARRAY_SIZE(mainscan)]; + unsigned int i, audio, nscan; + int max1,max2,carrier,rx,mode,lastmode,default_carrier; + + set_freezable(); + + for (;;) { + tvaudio_sleep(dev,-1); + if (kthread_should_stop()) + goto done; + + restart: + try_to_freeze(); + + dev->thread.scan1 = dev->thread.scan2; + dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); + dev->tvaudio = NULL; + + saa_writeb(SAA7134_MONITOR_SELECT, 0xa0); + saa_writeb(SAA7134_FM_DEMATRIX, 0x80); + + if (dev->ctl_automute) + dev->automute = 1; + + mute_input_7134(dev); + + /* give the tuner some time */ + if (tvaudio_sleep(dev,SCAN_INITIAL_DELAY)) + goto restart; + + max1 = 0; + max2 = 0; + nscan = 0; + carrier = 0; + default_carrier = 0; + for (i = 0; i < ARRAY_SIZE(mainscan); i++) { + if (!(dev->tvnorm->id & mainscan[i].std)) + continue; + if (!default_carrier) + default_carrier = mainscan[i].carr; + nscan++; + } + + if (1 == nscan) { + /* only one candidate -- skip scan ;) */ + dprintk("only one main carrier candidate - skipping scan\n"); + max1 = 12345; + carrier = default_carrier; + } else { + /* scan for the main carrier */ + saa_writeb(SAA7134_MONITOR_SELECT,0x00); + tvaudio_setmode(dev,&tvaudio[0],NULL); + for (i = 0; i < ARRAY_SIZE(mainscan); i++) { + carr_vals[i] = tvaudio_checkcarrier(dev, mainscan+i); + if (dev->thread.scan1 != dev->thread.scan2) + goto restart; + } + for (max1 = 0, max2 = 0, i = 0; i < ARRAY_SIZE(mainscan); i++) { + if (max1 < carr_vals[i]) { + max2 = max1; + max1 = carr_vals[i]; + carrier = mainscan[i].carr; + } else if (max2 < carr_vals[i]) { + max2 = carr_vals[i]; + } + } + } + + if (0 != carrier && max1 > 2000 && max1 > max2*3) { + /* found good carrier */ + dprintk("found %s main sound carrier @ %d.%03d MHz [%d/%d]\n", + dev->tvnorm->name, carrier/1000, carrier%1000, + max1, max2); + dev->last_carrier = carrier; + dev->automute = 0; + + } else if (0 != dev->last_carrier) { + /* no carrier -- try last detected one as fallback */ + carrier = dev->last_carrier; + dprintk("audio carrier scan failed, " + "using %d.%03d MHz [last detected]\n", + carrier/1000, carrier%1000); + dev->automute = 1; + + } else { + /* no carrier + no fallback -- use default */ + carrier = default_carrier; + dprintk("audio carrier scan failed, " + "using %d.%03d MHz [default]\n", + carrier/1000, carrier%1000); + dev->automute = 1; + } + tvaudio_setcarrier(dev,carrier,carrier); + saa_andorb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0x30, 0x00); + saa7134_tvaudio_setmute(dev); + /* find the exact tv audio norm */ + for (audio = UNSET, i = 0; i < TVAUDIO; i++) { + if (dev->tvnorm->id != UNSET && + !(dev->tvnorm->id & tvaudio[i].std)) + continue; + if (tvaudio[i].carr1 != carrier) + continue; + /* Note: at least the primary carrier is right here */ + if (UNSET == audio) + audio = i; + tvaudio_setmode(dev,&tvaudio[i],"trying"); + if (tvaudio_sleep(dev,SCAN_SUBCARRIER_DELAY)) + goto restart; + if (-1 != tvaudio_getstereo(dev,&tvaudio[i])) { + audio = i; + break; + } + } + saa_andorb(SAA7134_STEREO_DAC_OUTPUT_SELECT, 0x30, 0x30); + if (UNSET == audio) + continue; + tvaudio_setmode(dev,&tvaudio[audio],"using"); + + tvaudio_setstereo(dev,&tvaudio[audio],V4L2_TUNER_MODE_MONO); + dev->tvaudio = &tvaudio[audio]; + + lastmode = 42; + for (;;) { + + try_to_freeze(); + + if (tvaudio_sleep(dev,5000)) + goto restart; + if (kthread_should_stop()) + break; + if (UNSET == dev->thread.mode) { + rx = tvaudio_getstereo(dev, &tvaudio[audio]); + mode = saa7134_tvaudio_rx2mode(rx); + } else { + mode = dev->thread.mode; + } + if (lastmode != mode) { + tvaudio_setstereo(dev,&tvaudio[audio],mode); + lastmode = mode; + } + } + } + + done: + dev->thread.stopped = 1; + return 0; +} + +/* ------------------------------------------------------------------ */ +/* saa7133 / saa7135 code */ + +static char *stdres[0x20] = { + [0x00] = "no standard detected", + [0x01] = "B/G (in progress)", + [0x02] = "D/K (in progress)", + [0x03] = "M (in progress)", + + [0x04] = "B/G A2", + [0x05] = "B/G NICAM", + [0x06] = "D/K A2 (1)", + [0x07] = "D/K A2 (2)", + [0x08] = "D/K A2 (3)", + [0x09] = "D/K NICAM", + [0x0a] = "L NICAM", + [0x0b] = "I NICAM", + + [0x0c] = "M Korea", + [0x0d] = "M BTSC ", + [0x0e] = "M EIAJ", + + [0x0f] = "FM radio / IF 10.7 / 50 deemp", + [0x10] = "FM radio / IF 10.7 / 75 deemp", + [0x11] = "FM radio / IF sel / 50 deemp", + [0x12] = "FM radio / IF sel / 75 deemp", + + [0x13 ... 0x1e ] = "unknown", + [0x1f] = "??? [in progress]", +}; + +#define DSP_RETRY 32 +#define DSP_DELAY 16 +#define SAA7135_DSP_RWCLEAR_RERR 1 + +static inline int saa_dsp_reset_error_bit(struct saa7134_dev *dev) +{ + int state = saa_readb(SAA7135_DSP_RWSTATE); + if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) { + d2printk("%s: resetting error bit\n", dev->name); + saa_writeb(SAA7135_DSP_RWCLEAR, SAA7135_DSP_RWCLEAR_RERR); + } + return 0; +} + +static inline int saa_dsp_wait_bit(struct saa7134_dev *dev, int bit) +{ + int state, count = DSP_RETRY; + + state = saa_readb(SAA7135_DSP_RWSTATE); + if (unlikely(state & SAA7135_DSP_RWSTATE_ERR)) { + printk(KERN_WARNING "%s: dsp access error\n", dev->name); + saa_dsp_reset_error_bit(dev); + return -EIO; + } + while (0 == (state & bit)) { + if (unlikely(0 == count)) { + printk("%s: dsp access wait timeout [bit=%s]\n", + dev->name, + (bit & SAA7135_DSP_RWSTATE_WRR) ? "WRR" : + (bit & SAA7135_DSP_RWSTATE_RDB) ? "RDB" : + (bit & SAA7135_DSP_RWSTATE_IDA) ? "IDA" : + "???"); + return -EIO; + } + saa_wait(DSP_DELAY); + state = saa_readb(SAA7135_DSP_RWSTATE); + count--; + } + return 0; +} + + +int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value) +{ + int err; + + d2printk("dsp write reg 0x%x = 0x%06x\n",reg<<2,value); + err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR); + if (err < 0) + return err; + saa_writel(reg,value); + err = saa_dsp_wait_bit(dev,SAA7135_DSP_RWSTATE_WRR); + if (err < 0) + return err; + return 0; +} + +static int getstereo_7133(struct saa7134_dev *dev) +{ + int retval = V4L2_TUNER_SUB_MONO; + u32 value; + + value = saa_readl(0x528 >> 2); + if (value & 0x20) + retval = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + if (value & 0x40) + retval = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + return retval; +} + +static int mute_input_7133(struct saa7134_dev *dev) +{ + u32 reg = 0; + u32 xbarin, xbarout; + int mask; + struct saa7134_input *in; + + xbarin = 0x03; + switch (dev->input->amux) { + case TV: + reg = 0x02; + xbarin = 0; + break; + case LINE1: + reg = 0x00; + break; + case LINE2: + case LINE2_LEFT: + reg = 0x09; + break; + } + saa_dsp_writel(dev, 0x464 >> 2, xbarin); + if (dev->ctl_mute) { + reg = 0x07; + xbarout = 0xbbbbbb; + } else + xbarout = 0xbbbb10; + saa_dsp_writel(dev, 0x46c >> 2, xbarout); + + saa_writel(0x594 >> 2, reg); + + + /* switch gpio-connected external audio mux */ + if (0 != card(dev).gpiomask) { + mask = card(dev).gpiomask; + + if (card(dev).mute.name && dev->ctl_mute) + in = &card(dev).mute; + else + in = dev->input; + + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, mask, mask); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, mask, in->gpio); + saa7134_track_gpio(dev,in->name); + } + + return 0; +} + +static int tvaudio_thread_ddep(void *data) +{ + struct saa7134_dev *dev = data; + u32 value, norms; + + set_freezable(); + for (;;) { + tvaudio_sleep(dev,-1); + if (kthread_should_stop()) + goto done; + restart: + try_to_freeze(); + + dev->thread.scan1 = dev->thread.scan2; + dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); + + if (audio_ddep >= 0x04 && audio_ddep <= 0x0e) { + /* insmod option override */ + norms = (audio_ddep << 2) | 0x01; + dprintk("ddep override: %s\n",stdres[audio_ddep]); + } else if (&card(dev).radio == dev->input) { + dprintk("FM Radio\n"); + if (dev->tuner_type == TUNER_PHILIPS_TDA8290) { + norms = (0x11 << 2) | 0x01; + saa_dsp_writel(dev, 0x42c >> 2, 0x729555); + } else { + norms = (0x0f << 2) | 0x01; + } + } else { + /* (let chip) scan for sound carrier */ + norms = 0; + if (dev->tvnorm->id & (V4L2_STD_B | V4L2_STD_GH)) + norms |= 0x04; + if (dev->tvnorm->id & V4L2_STD_PAL_I) + norms |= 0x20; + if (dev->tvnorm->id & V4L2_STD_DK) + norms |= 0x08; + if (dev->tvnorm->id & V4L2_STD_MN) + norms |= 0x40; + if (dev->tvnorm->id & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) + norms |= 0x10; + if (0 == norms) + norms = 0x7c; /* all */ + dprintk("scanning:%s%s%s%s%s\n", + (norms & 0x04) ? " B/G" : "", + (norms & 0x08) ? " D/K" : "", + (norms & 0x10) ? " L/L'" : "", + (norms & 0x20) ? " I" : "", + (norms & 0x40) ? " M" : ""); + } + + /* kick automatic standard detection */ + saa_dsp_writel(dev, 0x454 >> 2, 0); + saa_dsp_writel(dev, 0x454 >> 2, norms | 0x80); + + /* setup crossbars */ + saa_dsp_writel(dev, 0x464 >> 2, 0x000000); + saa_dsp_writel(dev, 0x470 >> 2, 0x101010); + + if (tvaudio_sleep(dev,3000)) + goto restart; + value = saa_readl(0x528 >> 2) & 0xffffff; + + dprintk("tvaudio thread status: 0x%x [%s%s%s]\n", + value, stdres[value & 0x1f], + (value & 0x000020) ? ",stereo" : "", + (value & 0x000040) ? ",dual" : ""); + dprintk("detailed status: " + "%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s#%s\n", + (value & 0x000080) ? " A2/EIAJ pilot tone " : "", + (value & 0x000100) ? " A2/EIAJ dual " : "", + (value & 0x000200) ? " A2/EIAJ stereo " : "", + (value & 0x000400) ? " A2/EIAJ noise mute " : "", + + (value & 0x000800) ? " BTSC/FM radio pilot " : "", + (value & 0x001000) ? " SAP carrier " : "", + (value & 0x002000) ? " BTSC stereo noise mute " : "", + (value & 0x004000) ? " SAP noise mute " : "", + (value & 0x008000) ? " VDSP " : "", + + (value & 0x010000) ? " NICST " : "", + (value & 0x020000) ? " NICDU " : "", + (value & 0x040000) ? " NICAM muted " : "", + (value & 0x080000) ? " NICAM reserve sound " : "", + + (value & 0x100000) ? " init done " : ""); + } + + done: + dev->thread.stopped = 1; + return 0; +} + +/* ------------------------------------------------------------------ */ +/* common stuff + external entry points */ + +void saa7134_enable_i2s(struct saa7134_dev *dev) +{ + int i2s_format; + + if (!card_is_empress(dev)) + return; + + if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130) + return; + + /* configure GPIO for out */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0E000000, 0x00000000); + + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + /* Set I2S format (SONY) Â */ + saa_writeb(SAA7133_I2S_AUDIO_CONTROL, 0x00); + /* Start I2S */ + saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x11); + break; + + case PCI_DEVICE_ID_PHILIPS_SAA7134: + i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01; + + /* enable I2S audio output for the mpeg encoder */ + saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80); + saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, i2s_format); + saa_writeb(SAA7134_I2S_OUTPUT_LEVEL, 0x0F); + saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x01); + + default: + break; + } +} + +int saa7134_tvaudio_rx2mode(u32 rx) +{ + u32 mode; + + mode = V4L2_TUNER_MODE_MONO; + if (rx & V4L2_TUNER_SUB_STEREO) + mode = V4L2_TUNER_MODE_STEREO; + else if (rx & V4L2_TUNER_SUB_LANG1) + mode = V4L2_TUNER_MODE_LANG1; + else if (rx & V4L2_TUNER_SUB_LANG2) + mode = V4L2_TUNER_MODE_LANG2; + return mode; +} + +void saa7134_tvaudio_setmute(struct saa7134_dev *dev) +{ + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7130: + case PCI_DEVICE_ID_PHILIPS_SAA7134: + mute_input_7134(dev); + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + mute_input_7133(dev); + break; + } +} + +void saa7134_tvaudio_setinput(struct saa7134_dev *dev, + struct saa7134_input *in) +{ + dev->input = in; + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7130: + case PCI_DEVICE_ID_PHILIPS_SAA7134: + mute_input_7134(dev); + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + mute_input_7133(dev); + break; + } + saa7134_enable_i2s(dev); +} + +void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level) +{ + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + saa_writeb(SAA7134_CHANNEL1_LEVEL, level & 0x1f); + saa_writeb(SAA7134_CHANNEL2_LEVEL, level & 0x1f); + saa_writeb(SAA7134_NICAM_LEVEL_ADJUST, level & 0x1f); + break; + } +} + +int saa7134_tvaudio_getstereo(struct saa7134_dev *dev) +{ + int retval = V4L2_TUNER_SUB_MONO; + + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + if (dev->tvaudio) + retval = tvaudio_getstereo(dev,dev->tvaudio); + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + retval = getstereo_7133(dev); + break; + } + return retval; +} + +void saa7134_tvaudio_init(struct saa7134_dev *dev) +{ + int clock = saa7134_boards[dev->board].audio_clock; + + if (UNSET != audio_clock_override) + clock = audio_clock_override; + + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + /* init all audio registers */ + saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x00); + if (need_resched()) + schedule(); + else + udelay(10); + + saa_writeb(SAA7134_AUDIO_CLOCK0, clock & 0xff); + saa_writeb(SAA7134_AUDIO_CLOCK1, (clock >> 8) & 0xff); + saa_writeb(SAA7134_AUDIO_CLOCK2, (clock >> 16) & 0xff); + /* frame locked audio is mandatory for NICAM */ + saa_writeb(SAA7134_AUDIO_PLL_CTRL, 0x01); + saa_writeb(SAA7134_NICAM_ERROR_LOW, 0x14); + saa_writeb(SAA7134_NICAM_ERROR_HIGH, 0x50); + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + saa_writel(0x598 >> 2, clock); + saa_dsp_writel(dev, 0x474 >> 2, 0x00); + saa_dsp_writel(dev, 0x450 >> 2, 0x00); + } +} + +int saa7134_tvaudio_init2(struct saa7134_dev *dev) +{ + int (*my_thread)(void *data) = NULL; + + switch (dev->pci->device) { + case PCI_DEVICE_ID_PHILIPS_SAA7134: + my_thread = tvaudio_thread; + break; + case PCI_DEVICE_ID_PHILIPS_SAA7133: + case PCI_DEVICE_ID_PHILIPS_SAA7135: + my_thread = tvaudio_thread_ddep; + break; + } + + dev->thread.thread = NULL; + dev->thread.scan1 = dev->thread.scan2 = 0; + if (my_thread) { + saa7134_tvaudio_init(dev); + /* start tvaudio thread */ + dev->thread.thread = kthread_run(my_thread, dev, "%s", dev->name); + if (IS_ERR(dev->thread.thread)) { + printk(KERN_WARNING "%s: kernel_thread() failed\n", + dev->name); + /* XXX: missing error handling here */ + } + } + + saa7134_enable_i2s(dev); + return 0; +} + +int saa7134_tvaudio_close(struct saa7134_dev *dev) +{ + dev->automute = 1; + /* anything else to undo? */ + return 0; +} + +int saa7134_tvaudio_fini(struct saa7134_dev *dev) +{ + /* shutdown tvaudio thread */ + if (dev->thread.thread && !dev->thread.stopped) + kthread_stop(dev->thread.thread); + + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, 0x00); /* LINE1 */ + return 0; +} + +int saa7134_tvaudio_do_scan(struct saa7134_dev *dev) +{ + if (dev->input->amux != TV) { + dprintk("sound IF not in use, skipping scan\n"); + dev->automute = 0; + saa7134_tvaudio_setmute(dev); + } else if (dev->thread.thread) { + dev->thread.mode = UNSET; + dev->thread.scan2++; + + if (!dev->insuspend && !dev->thread.stopped) + wake_up_process(dev->thread.thread); + } else { + dev->automute = 0; + saa7134_tvaudio_setmute(dev); + } + return 0; +} + +EXPORT_SYMBOL(saa_dsp_writel); +EXPORT_SYMBOL(saa7134_tvaudio_setmute); + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/pci/saa7134/saa7134-vbi.c b/drivers/media/pci/saa7134/saa7134-vbi.c new file mode 100644 index 000000000000..e9aa94b807f1 --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-vbi.c @@ -0,0 +1,255 @@ +/* + * + * device driver for philips saa7134 based TV cards + * video4linux video interface + * + * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> [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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> + +#include "saa7134-reg.h" +#include "saa7134.h" + +/* ------------------------------------------------------------------ */ + +static unsigned int vbi_debug; +module_param(vbi_debug, int, 0644); +MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]"); + +static unsigned int vbibufs = 4; +module_param(vbibufs, int, 0444); +MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32"); + +#define dprintk(fmt, arg...) if (vbi_debug) \ + printk(KERN_DEBUG "%s/vbi: " fmt, dev->name , ## arg) + +/* ------------------------------------------------------------------ */ + +#define VBI_LINE_COUNT 16 +#define VBI_LINE_LENGTH 2048 +#define VBI_SCALE 0x200 + +static void task_init(struct saa7134_dev *dev, struct saa7134_buf *buf, + int task) +{ + struct saa7134_tvnorm *norm = dev->tvnorm; + + /* setup video scaler */ + saa_writeb(SAA7134_VBI_H_START1(task), norm->h_start & 0xff); + saa_writeb(SAA7134_VBI_H_START2(task), norm->h_start >> 8); + saa_writeb(SAA7134_VBI_H_STOP1(task), norm->h_stop & 0xff); + saa_writeb(SAA7134_VBI_H_STOP2(task), norm->h_stop >> 8); + saa_writeb(SAA7134_VBI_V_START1(task), norm->vbi_v_start_0 & 0xff); + saa_writeb(SAA7134_VBI_V_START2(task), norm->vbi_v_start_0 >> 8); + saa_writeb(SAA7134_VBI_V_STOP1(task), norm->vbi_v_stop_0 & 0xff); + saa_writeb(SAA7134_VBI_V_STOP2(task), norm->vbi_v_stop_0 >> 8); + + saa_writeb(SAA7134_VBI_H_SCALE_INC1(task), VBI_SCALE & 0xff); + saa_writeb(SAA7134_VBI_H_SCALE_INC2(task), VBI_SCALE >> 8); + saa_writeb(SAA7134_VBI_PHASE_OFFSET_LUMA(task), 0x00); + saa_writeb(SAA7134_VBI_PHASE_OFFSET_CHROMA(task), 0x00); + + saa_writeb(SAA7134_VBI_H_LEN1(task), buf->vb.width & 0xff); + saa_writeb(SAA7134_VBI_H_LEN2(task), buf->vb.width >> 8); + saa_writeb(SAA7134_VBI_V_LEN1(task), buf->vb.height & 0xff); + saa_writeb(SAA7134_VBI_V_LEN2(task), buf->vb.height >> 8); + + saa_andorb(SAA7134_DATA_PATH(task), 0xc0, 0x00); +} + +/* ------------------------------------------------------------------ */ + +static int buffer_activate(struct saa7134_dev *dev, + struct saa7134_buf *buf, + struct saa7134_buf *next) +{ + unsigned long control,base; + + dprintk("buffer_activate [%p]\n",buf); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->top_seen = 0; + + task_init(dev,buf,TASK_A); + task_init(dev,buf,TASK_B); + saa_writeb(SAA7134_OFMT_DATA_A, 0x06); + saa_writeb(SAA7134_OFMT_DATA_B, 0x06); + + /* DMA: setup channel 2+3 (= VBI Task A+B) */ + base = saa7134_buffer_base(buf); + control = SAA7134_RS_CONTROL_BURST_16 | + SAA7134_RS_CONTROL_ME | + (buf->pt->dma >> 12); + saa_writel(SAA7134_RS_BA1(2),base); + saa_writel(SAA7134_RS_BA2(2),base + buf->vb.size/2); + saa_writel(SAA7134_RS_PITCH(2),buf->vb.width); + saa_writel(SAA7134_RS_CONTROL(2),control); + saa_writel(SAA7134_RS_BA1(3),base); + saa_writel(SAA7134_RS_BA2(3),base + buf->vb.size/2); + saa_writel(SAA7134_RS_PITCH(3),buf->vb.width); + saa_writel(SAA7134_RS_CONTROL(3),control); + + /* start DMA */ + saa7134_set_dmabits(dev); + mod_timer(&dev->vbi_q.timeout, jiffies+BUFFER_TIMEOUT); + + return 0; +} + +static int buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct saa7134_fh *fh = q->priv_data; + struct saa7134_dev *dev = fh->dev; + struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + struct saa7134_tvnorm *norm = dev->tvnorm; + unsigned int lines, llength, size; + int err; + + lines = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1; + if (lines > VBI_LINE_COUNT) + lines = VBI_LINE_COUNT; + llength = VBI_LINE_LENGTH; + size = lines * llength * 2; + if (0 != buf->vb.baddr && buf->vb.bsize < size) + return -EINVAL; + + if (buf->vb.size != size) + saa7134_dma_free(q,buf); + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + + buf->vb.width = llength; + buf->vb.height = lines; + buf->vb.size = size; + buf->pt = &fh->pt_vbi; + + err = videobuf_iolock(q,&buf->vb,NULL); + if (err) + goto oops; + err = saa7134_pgtable_build(dev->pci,buf->pt, + dma->sglist, + dma->sglen, + saa7134_buffer_startpage(buf)); + if (err) + goto oops; + } + buf->vb.state = VIDEOBUF_PREPARED; + buf->activate = buffer_activate; + buf->vb.field = field; + return 0; + + oops: + saa7134_dma_free(q,buf); + return err; +} + +static int +buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + struct saa7134_fh *fh = q->priv_data; + struct saa7134_dev *dev = fh->dev; + int llength,lines; + + lines = dev->tvnorm->vbi_v_stop_0 - dev->tvnorm->vbi_v_start_0 +1; + llength = VBI_LINE_LENGTH; + *size = lines * llength * 2; + if (0 == *count) + *count = vbibufs; + *count = saa7134_buffer_count(*size,*count); + return 0; +} + +static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct saa7134_fh *fh = q->priv_data; + struct saa7134_dev *dev = fh->dev; + struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + + saa7134_buffer_queue(dev,&dev->vbi_q,buf); +} + +static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + + saa7134_dma_free(q,buf); +} + +struct videobuf_queue_ops saa7134_vbi_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ */ + +int saa7134_vbi_init1(struct saa7134_dev *dev) +{ + INIT_LIST_HEAD(&dev->vbi_q.queue); + init_timer(&dev->vbi_q.timeout); + dev->vbi_q.timeout.function = saa7134_buffer_timeout; + dev->vbi_q.timeout.data = (unsigned long)(&dev->vbi_q); + dev->vbi_q.dev = dev; + + if (vbibufs < 2) + vbibufs = 2; + if (vbibufs > VIDEO_MAX_FRAME) + vbibufs = VIDEO_MAX_FRAME; + return 0; +} + +int saa7134_vbi_fini(struct saa7134_dev *dev) +{ + /* nothing */ + return 0; +} + +void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status) +{ + spin_lock(&dev->slock); + if (dev->vbi_q.curr) { + dev->vbi_fieldcount++; + /* make sure we have seen both fields */ + if ((status & 0x10) == 0x00) { + dev->vbi_q.curr->top_seen = 1; + goto done; + } + if (!dev->vbi_q.curr->top_seen) + goto done; + + dev->vbi_q.curr->vb.field_count = dev->vbi_fieldcount; + saa7134_buffer_finish(dev,&dev->vbi_q,VIDEOBUF_DONE); + } + saa7134_buffer_next(dev,&dev->vbi_q); + + done: + spin_unlock(&dev->slock); +} + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c new file mode 100644 index 000000000000..22f8758d047f --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134-video.c @@ -0,0 +1,2661 @@ +/* + * + * device driver for philips saa7134 based TV cards + * video4linux video interface + * + * (c) 2001-03 Gerd Knorr <kraxel@bytesex.org> [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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/sort.h> + +#include "saa7134-reg.h" +#include "saa7134.h" +#include <media/v4l2-common.h> +#include <media/saa6588.h> + +/* ------------------------------------------------------------------ */ + +unsigned int video_debug; +static unsigned int gbuffers = 8; +static unsigned int noninterlaced; /* 0 */ +static unsigned int gbufsize = 720*576*4; +static unsigned int gbufsize_max = 720*576*4; +static char secam[] = "--"; +module_param(video_debug, int, 0644); +MODULE_PARM_DESC(video_debug,"enable debug messages [video]"); +module_param(gbuffers, int, 0444); +MODULE_PARM_DESC(gbuffers,"number of capture buffers, range 2-32"); +module_param(noninterlaced, int, 0644); +MODULE_PARM_DESC(noninterlaced,"capture non interlaced video"); +module_param_string(secam, secam, sizeof(secam), 0644); +MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc"); + + +#define dprintk(fmt, arg...) if (video_debug&0x04) \ + printk(KERN_DEBUG "%s/video: " fmt, dev->name , ## arg) + +/* ------------------------------------------------------------------ */ +/* Defines for Video Output Port Register at address 0x191 */ + +/* Bit 0: VIP code T bit polarity */ + +#define VP_T_CODE_P_NON_INVERTED 0x00 +#define VP_T_CODE_P_INVERTED 0x01 + +/* ------------------------------------------------------------------ */ +/* Defines for Video Output Port Register at address 0x195 */ + +/* Bit 2: Video output clock delay control */ + +#define VP_CLK_CTRL2_NOT_DELAYED 0x00 +#define VP_CLK_CTRL2_DELAYED 0x04 + +/* Bit 1: Video output clock invert control */ + +#define VP_CLK_CTRL1_NON_INVERTED 0x00 +#define VP_CLK_CTRL1_INVERTED 0x02 + +/* ------------------------------------------------------------------ */ +/* Defines for Video Output Port Register at address 0x196 */ + +/* Bits 2 to 0: VSYNC pin video vertical sync type */ + +#define VP_VS_TYPE_MASK 0x07 + +#define VP_VS_TYPE_OFF 0x00 +#define VP_VS_TYPE_V123 0x01 +#define VP_VS_TYPE_V_ITU 0x02 +#define VP_VS_TYPE_VGATE_L 0x03 +#define VP_VS_TYPE_RESERVED1 0x04 +#define VP_VS_TYPE_RESERVED2 0x05 +#define VP_VS_TYPE_F_ITU 0x06 +#define VP_VS_TYPE_SC_FID 0x07 + +/* ------------------------------------------------------------------ */ +/* data structs for video */ + +static int video_out[][9] = { + [CCIR656] = { 0x00, 0xb1, 0x00, 0xa1, 0x00, 0x04, 0x06, 0x00, 0x00 }, +}; + +static struct saa7134_format formats[] = { + { + .name = "8 bpp gray", + .fourcc = V4L2_PIX_FMT_GREY, + .depth = 8, + .pm = 0x06, + },{ + .name = "15 bpp RGB, le", + .fourcc = V4L2_PIX_FMT_RGB555, + .depth = 16, + .pm = 0x13 | 0x80, + },{ + .name = "15 bpp RGB, be", + .fourcc = V4L2_PIX_FMT_RGB555X, + .depth = 16, + .pm = 0x13 | 0x80, + .bswap = 1, + },{ + .name = "16 bpp RGB, le", + .fourcc = V4L2_PIX_FMT_RGB565, + .depth = 16, + .pm = 0x10 | 0x80, + },{ + .name = "16 bpp RGB, be", + .fourcc = V4L2_PIX_FMT_RGB565X, + .depth = 16, + .pm = 0x10 | 0x80, + .bswap = 1, + },{ + .name = "24 bpp RGB, le", + .fourcc = V4L2_PIX_FMT_BGR24, + .depth = 24, + .pm = 0x11, + },{ + .name = "24 bpp RGB, be", + .fourcc = V4L2_PIX_FMT_RGB24, + .depth = 24, + .pm = 0x11, + .bswap = 1, + },{ + .name = "32 bpp RGB, le", + .fourcc = V4L2_PIX_FMT_BGR32, + .depth = 32, + .pm = 0x12, + },{ + .name = "32 bpp RGB, be", + .fourcc = V4L2_PIX_FMT_RGB32, + .depth = 32, + .pm = 0x12, + .bswap = 1, + .wswap = 1, + },{ + .name = "4:2:2 packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .pm = 0x00, + .bswap = 1, + .yuv = 1, + },{ + .name = "4:2:2 packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .pm = 0x00, + .yuv = 1, + },{ + .name = "4:2:2 planar, Y-Cb-Cr", + .fourcc = V4L2_PIX_FMT_YUV422P, + .depth = 16, + .pm = 0x09, + .yuv = 1, + .planar = 1, + .hshift = 1, + .vshift = 0, + },{ + .name = "4:2:0 planar, Y-Cb-Cr", + .fourcc = V4L2_PIX_FMT_YUV420, + .depth = 12, + .pm = 0x0a, + .yuv = 1, + .planar = 1, + .hshift = 1, + .vshift = 1, + },{ + .name = "4:2:0 planar, Y-Cb-Cr", + .fourcc = V4L2_PIX_FMT_YVU420, + .depth = 12, + .pm = 0x0a, + .yuv = 1, + .planar = 1, + .uvswap = 1, + .hshift = 1, + .vshift = 1, + } +}; +#define FORMATS ARRAY_SIZE(formats) + +#define NORM_625_50 \ + .h_start = 0, \ + .h_stop = 719, \ + .video_v_start = 24, \ + .video_v_stop = 311, \ + .vbi_v_start_0 = 7, \ + .vbi_v_stop_0 = 22, \ + .vbi_v_start_1 = 319, \ + .src_timing = 4 + +#define NORM_525_60 \ + .h_start = 0, \ + .h_stop = 719, \ + .video_v_start = 23, \ + .video_v_stop = 262, \ + .vbi_v_start_0 = 10, \ + .vbi_v_stop_0 = 21, \ + .vbi_v_start_1 = 273, \ + .src_timing = 7 + +static struct saa7134_tvnorm tvnorms[] = { + { + .name = "PAL", /* autodetect */ + .id = V4L2_STD_PAL, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0x81, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + + },{ + .name = "PAL-BG", + .id = V4L2_STD_PAL_BG, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0x81, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + + },{ + .name = "PAL-I", + .id = V4L2_STD_PAL_I, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0x81, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + + },{ + .name = "PAL-DK", + .id = V4L2_STD_PAL_DK, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0x81, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + + },{ + .name = "NTSC", + .id = V4L2_STD_NTSC, + NORM_525_60, + + .sync_control = 0x59, + .luma_control = 0x40, + .chroma_ctrl1 = 0x89, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x0e, + .vgate_misc = 0x18, + + },{ + .name = "SECAM", + .id = V4L2_STD_SECAM, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x1b, + .chroma_ctrl1 = 0xd1, + .chroma_gain = 0x80, + .chroma_ctrl2 = 0x00, + .vgate_misc = 0x1c, + + },{ + .name = "SECAM-DK", + .id = V4L2_STD_SECAM_DK, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x1b, + .chroma_ctrl1 = 0xd1, + .chroma_gain = 0x80, + .chroma_ctrl2 = 0x00, + .vgate_misc = 0x1c, + + },{ + .name = "SECAM-L", + .id = V4L2_STD_SECAM_L, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x1b, + .chroma_ctrl1 = 0xd1, + .chroma_gain = 0x80, + .chroma_ctrl2 = 0x00, + .vgate_misc = 0x1c, + + },{ + .name = "SECAM-Lc", + .id = V4L2_STD_SECAM_LC, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x1b, + .chroma_ctrl1 = 0xd1, + .chroma_gain = 0x80, + .chroma_ctrl2 = 0x00, + .vgate_misc = 0x1c, + + },{ + .name = "PAL-M", + .id = V4L2_STD_PAL_M, + NORM_525_60, + + .sync_control = 0x59, + .luma_control = 0x40, + .chroma_ctrl1 = 0xb9, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x0e, + .vgate_misc = 0x18, + + },{ + .name = "PAL-Nc", + .id = V4L2_STD_PAL_Nc, + NORM_625_50, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0xa1, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + + },{ + .name = "PAL-60", + .id = V4L2_STD_PAL_60, + + .h_start = 0, + .h_stop = 719, + .video_v_start = 23, + .video_v_stop = 262, + .vbi_v_start_0 = 10, + .vbi_v_stop_0 = 21, + .vbi_v_start_1 = 273, + .src_timing = 7, + + .sync_control = 0x18, + .luma_control = 0x40, + .chroma_ctrl1 = 0x81, + .chroma_gain = 0x2a, + .chroma_ctrl2 = 0x06, + .vgate_misc = 0x1c, + } +}; +#define TVNORMS ARRAY_SIZE(tvnorms) + +#define V4L2_CID_PRIVATE_INVERT (V4L2_CID_PRIVATE_BASE + 0) +#define V4L2_CID_PRIVATE_Y_ODD (V4L2_CID_PRIVATE_BASE + 1) +#define V4L2_CID_PRIVATE_Y_EVEN (V4L2_CID_PRIVATE_BASE + 2) +#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 4) + +static const struct v4l2_queryctrl no_ctrl = { + .name = "42", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; +static const struct v4l2_queryctrl video_ctrls[] = { + /* --- video --- */ + { + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 128, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 68, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 64, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_HUE, + .name = "Hue", + .minimum = -128, + .maximum = 127, + .step = 1, + .default_value = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_HFLIP, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, + /* --- audio --- */ + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = -15, + .maximum = 15, + .step = 1, + .default_value = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + /* --- private --- */ + { + .id = V4L2_CID_PRIVATE_INVERT, + .name = "Invert", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_PRIVATE_Y_ODD, + .name = "y offset odd field", + .minimum = 0, + .maximum = 128, + .step = 1, + .default_value = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_PRIVATE_Y_EVEN, + .name = "y offset even field", + .minimum = 0, + .maximum = 128, + .step = 1, + .default_value = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_PRIVATE_AUTOMUTE, + .name = "automute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + } +}; +static const unsigned int CTRLS = ARRAY_SIZE(video_ctrls); + +static const struct v4l2_queryctrl* ctrl_by_id(unsigned int id) +{ + unsigned int i; + + for (i = 0; i < CTRLS; i++) + if (video_ctrls[i].id == id) + return video_ctrls+i; + return NULL; +} + +static struct saa7134_format* format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < FORMATS; i++) + if (formats[i].fourcc == fourcc) + return formats+i; + return NULL; +} + +/* ----------------------------------------------------------------------- */ +/* resource management */ + +static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bit) +{ + if (fh->resources & bit) + /* have it already allocated */ + return 1; + + /* is it free? */ + mutex_lock(&dev->lock); + if (dev->resources & bit) { + /* no, someone else uses it */ + mutex_unlock(&dev->lock); + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + dev->resources |= bit; + dprintk("res: get %d\n",bit); + mutex_unlock(&dev->lock); + return 1; +} + +static int res_check(struct saa7134_fh *fh, unsigned int bit) +{ + return (fh->resources & bit); +} + +static int res_locked(struct saa7134_dev *dev, unsigned int bit) +{ + return (dev->resources & bit); +} + +static +void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits) +{ + BUG_ON((fh->resources & bits) != bits); + + mutex_lock(&dev->lock); + fh->resources &= ~bits; + dev->resources &= ~bits; + dprintk("res: put %d\n",bits); + mutex_unlock(&dev->lock); +} + +/* ------------------------------------------------------------------ */ + +static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) +{ + dprintk("set tv norm = %s\n",norm->name); + dev->tvnorm = norm; + + /* setup cropping */ + dev->crop_bounds.left = norm->h_start; + dev->crop_defrect.left = norm->h_start; + dev->crop_bounds.width = norm->h_stop - norm->h_start +1; + dev->crop_defrect.width = norm->h_stop - norm->h_start +1; + + dev->crop_bounds.top = (norm->vbi_v_stop_0+1)*2; + dev->crop_defrect.top = norm->video_v_start*2; + dev->crop_bounds.height = ((norm->id & V4L2_STD_525_60) ? 524 : 624) + - dev->crop_bounds.top; + dev->crop_defrect.height = (norm->video_v_stop - norm->video_v_start +1)*2; + + dev->crop_current = dev->crop_defrect; + + saa7134_set_tvnorm_hw(dev); +} + +static void video_mux(struct saa7134_dev *dev, int input) +{ + dprintk("video input = %d [%s]\n", input, card_in(dev, input).name); + dev->ctl_input = input; + set_tvnorm(dev, dev->tvnorm); + saa7134_tvaudio_setinput(dev, &card_in(dev, input)); +} + + +static void saa7134_set_decoder(struct saa7134_dev *dev) +{ + int luma_control, sync_control, mux; + + struct saa7134_tvnorm *norm = dev->tvnorm; + mux = card_in(dev, dev->ctl_input).vmux; + + luma_control = norm->luma_control; + sync_control = norm->sync_control; + + if (mux > 5) + luma_control |= 0x80; /* svideo */ + if (noninterlaced || dev->nosignal) + sync_control |= 0x20; + + /* setup video decoder */ + saa_writeb(SAA7134_INCR_DELAY, 0x08); + saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux); + saa_writeb(SAA7134_ANALOG_IN_CTRL2, 0x00); + + saa_writeb(SAA7134_ANALOG_IN_CTRL3, 0x90); + saa_writeb(SAA7134_ANALOG_IN_CTRL4, 0x90); + saa_writeb(SAA7134_HSYNC_START, 0xeb); + saa_writeb(SAA7134_HSYNC_STOP, 0xe0); + saa_writeb(SAA7134_SOURCE_TIMING1, norm->src_timing); + + saa_writeb(SAA7134_SYNC_CTRL, sync_control); + saa_writeb(SAA7134_LUMA_CTRL, luma_control); + saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright); + + saa_writeb(SAA7134_DEC_LUMA_CONTRAST, + dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast); + + saa_writeb(SAA7134_DEC_CHROMA_SATURATION, + dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); + + saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); + saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1); + saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain); + + saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2); + saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00); + + saa_writeb(SAA7134_ANALOG_ADC, 0x01); + saa_writeb(SAA7134_VGATE_START, 0x11); + saa_writeb(SAA7134_VGATE_STOP, 0xfe); + saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc); + saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); + saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80); +} + +void saa7134_set_tvnorm_hw(struct saa7134_dev *dev) +{ + saa7134_set_decoder(dev); + + if (card_in(dev, dev->ctl_input).tv) + saa_call_all(dev, core, s_std, dev->tvnorm->id); + /* Set the correct norm for the saa6752hs. This function + does nothing if there is no saa6752hs. */ + saa_call_empress(dev, core, s_std, dev->tvnorm->id); +} + +static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale) +{ + static const struct { + int xpsc; + int xacl; + int xc2_1; + int xdcg; + int vpfy; + } vals[] = { + /* XPSC XACL XC2_1 XDCG VPFY */ + { 1, 0, 0, 0, 0 }, + { 2, 2, 1, 2, 2 }, + { 3, 4, 1, 3, 2 }, + { 4, 8, 1, 4, 2 }, + { 5, 8, 1, 4, 2 }, + { 6, 8, 1, 4, 3 }, + { 7, 8, 1, 4, 3 }, + { 8, 15, 0, 4, 3 }, + { 9, 15, 0, 4, 3 }, + { 10, 16, 1, 5, 3 }, + }; + static const int count = ARRAY_SIZE(vals); + int i; + + for (i = 0; i < count; i++) + if (vals[i].xpsc == prescale) + break; + if (i == count) + return; + + saa_writeb(SAA7134_H_PRESCALE(task), vals[i].xpsc); + saa_writeb(SAA7134_ACC_LENGTH(task), vals[i].xacl); + saa_writeb(SAA7134_LEVEL_CTRL(task), + (vals[i].xc2_1 << 3) | (vals[i].xdcg)); + saa_andorb(SAA7134_FIR_PREFILTER_CTRL(task), 0x0f, + (vals[i].vpfy << 2) | vals[i].vpfy); +} + +static void set_v_scale(struct saa7134_dev *dev, int task, int yscale) +{ + int val,mirror; + + saa_writeb(SAA7134_V_SCALE_RATIO1(task), yscale & 0xff); + saa_writeb(SAA7134_V_SCALE_RATIO2(task), yscale >> 8); + + mirror = (dev->ctl_mirror) ? 0x02 : 0x00; + if (yscale < 2048) { + /* LPI */ + dprintk("yscale LPI yscale=%d\n",yscale); + saa_writeb(SAA7134_V_FILTER(task), 0x00 | mirror); + saa_writeb(SAA7134_LUMA_CONTRAST(task), 0x40); + saa_writeb(SAA7134_CHROMA_SATURATION(task), 0x40); + } else { + /* ACM */ + val = 0x40 * 1024 / yscale; + dprintk("yscale ACM yscale=%d val=0x%x\n",yscale,val); + saa_writeb(SAA7134_V_FILTER(task), 0x01 | mirror); + saa_writeb(SAA7134_LUMA_CONTRAST(task), val); + saa_writeb(SAA7134_CHROMA_SATURATION(task), val); + } + saa_writeb(SAA7134_LUMA_BRIGHT(task), 0x80); +} + +static void set_size(struct saa7134_dev *dev, int task, + int width, int height, int interlace) +{ + int prescale,xscale,yscale,y_even,y_odd; + int h_start, h_stop, v_start, v_stop; + int div = interlace ? 2 : 1; + + /* setup video scaler */ + h_start = dev->crop_current.left; + v_start = dev->crop_current.top/2; + h_stop = (dev->crop_current.left + dev->crop_current.width -1); + v_stop = (dev->crop_current.top + dev->crop_current.height -1)/2; + + saa_writeb(SAA7134_VIDEO_H_START1(task), h_start & 0xff); + saa_writeb(SAA7134_VIDEO_H_START2(task), h_start >> 8); + saa_writeb(SAA7134_VIDEO_H_STOP1(task), h_stop & 0xff); + saa_writeb(SAA7134_VIDEO_H_STOP2(task), h_stop >> 8); + saa_writeb(SAA7134_VIDEO_V_START1(task), v_start & 0xff); + saa_writeb(SAA7134_VIDEO_V_START2(task), v_start >> 8); + saa_writeb(SAA7134_VIDEO_V_STOP1(task), v_stop & 0xff); + saa_writeb(SAA7134_VIDEO_V_STOP2(task), v_stop >> 8); + + prescale = dev->crop_current.width / width; + if (0 == prescale) + prescale = 1; + xscale = 1024 * dev->crop_current.width / prescale / width; + yscale = 512 * div * dev->crop_current.height / height; + dprintk("prescale=%d xscale=%d yscale=%d\n",prescale,xscale,yscale); + set_h_prescale(dev,task,prescale); + saa_writeb(SAA7134_H_SCALE_INC1(task), xscale & 0xff); + saa_writeb(SAA7134_H_SCALE_INC2(task), xscale >> 8); + set_v_scale(dev,task,yscale); + + saa_writeb(SAA7134_VIDEO_PIXELS1(task), width & 0xff); + saa_writeb(SAA7134_VIDEO_PIXELS2(task), width >> 8); + saa_writeb(SAA7134_VIDEO_LINES1(task), height/div & 0xff); + saa_writeb(SAA7134_VIDEO_LINES2(task), height/div >> 8); + + /* deinterlace y offsets */ + y_odd = dev->ctl_y_odd; + y_even = dev->ctl_y_even; + saa_writeb(SAA7134_V_PHASE_OFFSET0(task), y_odd); + saa_writeb(SAA7134_V_PHASE_OFFSET1(task), y_even); + saa_writeb(SAA7134_V_PHASE_OFFSET2(task), y_odd); + saa_writeb(SAA7134_V_PHASE_OFFSET3(task), y_even); +} + +/* ------------------------------------------------------------------ */ + +struct cliplist { + __u16 position; + __u8 enable; + __u8 disable; +}; + +static void set_cliplist(struct saa7134_dev *dev, int reg, + struct cliplist *cl, int entries, char *name) +{ + __u8 winbits = 0; + int i; + + for (i = 0; i < entries; i++) { + winbits |= cl[i].enable; + winbits &= ~cl[i].disable; + if (i < 15 && cl[i].position == cl[i+1].position) + continue; + saa_writeb(reg + 0, winbits); + saa_writeb(reg + 2, cl[i].position & 0xff); + saa_writeb(reg + 3, cl[i].position >> 8); + dprintk("clip: %s winbits=%02x pos=%d\n", + name,winbits,cl[i].position); + reg += 8; + } + for (; reg < 0x400; reg += 8) { + saa_writeb(reg+ 0, 0); + saa_writeb(reg + 1, 0); + saa_writeb(reg + 2, 0); + saa_writeb(reg + 3, 0); + } +} + +static int clip_range(int val) +{ + if (val < 0) + val = 0; + return val; +} + +/* Sort into smallest position first order */ +static int cliplist_cmp(const void *a, const void *b) +{ + const struct cliplist *cla = a; + const struct cliplist *clb = b; + if (cla->position < clb->position) + return -1; + if (cla->position > clb->position) + return 1; + return 0; +} + +static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips, + int nclips, int interlace) +{ + struct cliplist col[16], row[16]; + int cols = 0, rows = 0, i; + int div = interlace ? 2 : 1; + + memset(col, 0, sizeof(col)); + memset(row, 0, sizeof(row)); + for (i = 0; i < nclips && i < 8; i++) { + col[cols].position = clip_range(clips[i].c.left); + col[cols].enable = (1 << i); + cols++; + col[cols].position = clip_range(clips[i].c.left+clips[i].c.width); + col[cols].disable = (1 << i); + cols++; + row[rows].position = clip_range(clips[i].c.top / div); + row[rows].enable = (1 << i); + rows++; + row[rows].position = clip_range((clips[i].c.top + clips[i].c.height) + / div); + row[rows].disable = (1 << i); + rows++; + } + sort(col, cols, sizeof col[0], cliplist_cmp, NULL); + sort(row, rows, sizeof row[0], cliplist_cmp, NULL); + set_cliplist(dev,0x380,col,cols,"cols"); + set_cliplist(dev,0x384,row,rows,"rows"); + return 0; +} + +static int verify_preview(struct saa7134_dev *dev, struct v4l2_window *win) +{ + enum v4l2_field field; + int maxw, maxh; + + if (NULL == dev->ovbuf.base) + return -EINVAL; + if (NULL == dev->ovfmt) + return -EINVAL; + if (win->w.width < 48 || win->w.height < 32) + return -EINVAL; + if (win->clipcount > 2048) + return -EINVAL; + + field = win->field; + maxw = dev->crop_current.width; + maxh = dev->crop_current.height; + + if (V4L2_FIELD_ANY == field) { + field = (win->w.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_TOP; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + win->field = field; + if (win->w.width > maxw) + win->w.width = maxw; + if (win->w.height > maxh) + win->w.height = maxh; + return 0; +} + +static int start_preview(struct saa7134_dev *dev, struct saa7134_fh *fh) +{ + unsigned long base,control,bpl; + int err; + + err = verify_preview(dev,&fh->win); + if (0 != err) + return err; + + dev->ovfield = fh->win.field; + dprintk("start_preview %dx%d+%d+%d %s field=%s\n", + fh->win.w.width,fh->win.w.height, + fh->win.w.left,fh->win.w.top, + dev->ovfmt->name,v4l2_field_names[dev->ovfield]); + + /* setup window + clipping */ + set_size(dev,TASK_B,fh->win.w.width,fh->win.w.height, + V4L2_FIELD_HAS_BOTH(dev->ovfield)); + setup_clipping(dev,fh->clips,fh->nclips, + V4L2_FIELD_HAS_BOTH(dev->ovfield)); + if (dev->ovfmt->yuv) + saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x03); + else + saa_andorb(SAA7134_DATA_PATH(TASK_B), 0x3f, 0x01); + saa_writeb(SAA7134_OFMT_VIDEO_B, dev->ovfmt->pm | 0x20); + + /* dma: setup channel 1 (= Video Task B) */ + base = (unsigned long)dev->ovbuf.base; + base += dev->ovbuf.fmt.bytesperline * fh->win.w.top; + base += dev->ovfmt->depth/8 * fh->win.w.left; + bpl = dev->ovbuf.fmt.bytesperline; + control = SAA7134_RS_CONTROL_BURST_16; + if (dev->ovfmt->bswap) + control |= SAA7134_RS_CONTROL_BSWAP; + if (dev->ovfmt->wswap) + control |= SAA7134_RS_CONTROL_WSWAP; + if (V4L2_FIELD_HAS_BOTH(dev->ovfield)) { + saa_writel(SAA7134_RS_BA1(1),base); + saa_writel(SAA7134_RS_BA2(1),base+bpl); + saa_writel(SAA7134_RS_PITCH(1),bpl*2); + saa_writel(SAA7134_RS_CONTROL(1),control); + } else { + saa_writel(SAA7134_RS_BA1(1),base); + saa_writel(SAA7134_RS_BA2(1),base); + saa_writel(SAA7134_RS_PITCH(1),bpl); + saa_writel(SAA7134_RS_CONTROL(1),control); + } + + /* start dma */ + dev->ovenable = 1; + saa7134_set_dmabits(dev); + + return 0; +} + +static int stop_preview(struct saa7134_dev *dev, struct saa7134_fh *fh) +{ + dev->ovenable = 0; + saa7134_set_dmabits(dev); + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int buffer_activate(struct saa7134_dev *dev, + struct saa7134_buf *buf, + struct saa7134_buf *next) +{ + unsigned long base,control,bpl; + unsigned long bpl_uv,lines_uv,base2,base3,tmp; /* planar */ + + dprintk("buffer_activate buf=%p\n",buf); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->top_seen = 0; + + set_size(dev,TASK_A,buf->vb.width,buf->vb.height, + V4L2_FIELD_HAS_BOTH(buf->vb.field)); + if (buf->fmt->yuv) + saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x03); + else + saa_andorb(SAA7134_DATA_PATH(TASK_A), 0x3f, 0x01); + saa_writeb(SAA7134_OFMT_VIDEO_A, buf->fmt->pm); + + /* DMA: setup channel 0 (= Video Task A0) */ + base = saa7134_buffer_base(buf); + if (buf->fmt->planar) + bpl = buf->vb.width; + else + bpl = (buf->vb.width * buf->fmt->depth) / 8; + control = SAA7134_RS_CONTROL_BURST_16 | + SAA7134_RS_CONTROL_ME | + (buf->pt->dma >> 12); + if (buf->fmt->bswap) + control |= SAA7134_RS_CONTROL_BSWAP; + if (buf->fmt->wswap) + control |= SAA7134_RS_CONTROL_WSWAP; + if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) { + /* interlaced */ + saa_writel(SAA7134_RS_BA1(0),base); + saa_writel(SAA7134_RS_BA2(0),base+bpl); + saa_writel(SAA7134_RS_PITCH(0),bpl*2); + } else { + /* non-interlaced */ + saa_writel(SAA7134_RS_BA1(0),base); + saa_writel(SAA7134_RS_BA2(0),base); + saa_writel(SAA7134_RS_PITCH(0),bpl); + } + saa_writel(SAA7134_RS_CONTROL(0),control); + + if (buf->fmt->planar) { + /* DMA: setup channel 4+5 (= planar task A) */ + bpl_uv = bpl >> buf->fmt->hshift; + lines_uv = buf->vb.height >> buf->fmt->vshift; + base2 = base + bpl * buf->vb.height; + base3 = base2 + bpl_uv * lines_uv; + if (buf->fmt->uvswap) + tmp = base2, base2 = base3, base3 = tmp; + dprintk("uv: bpl=%ld lines=%ld base2/3=%ld/%ld\n", + bpl_uv,lines_uv,base2,base3); + if (V4L2_FIELD_HAS_BOTH(buf->vb.field)) { + /* interlaced */ + saa_writel(SAA7134_RS_BA1(4),base2); + saa_writel(SAA7134_RS_BA2(4),base2+bpl_uv); + saa_writel(SAA7134_RS_PITCH(4),bpl_uv*2); + saa_writel(SAA7134_RS_BA1(5),base3); + saa_writel(SAA7134_RS_BA2(5),base3+bpl_uv); + saa_writel(SAA7134_RS_PITCH(5),bpl_uv*2); + } else { + /* non-interlaced */ + saa_writel(SAA7134_RS_BA1(4),base2); + saa_writel(SAA7134_RS_BA2(4),base2); + saa_writel(SAA7134_RS_PITCH(4),bpl_uv); + saa_writel(SAA7134_RS_BA1(5),base3); + saa_writel(SAA7134_RS_BA2(5),base3); + saa_writel(SAA7134_RS_PITCH(5),bpl_uv); + } + saa_writel(SAA7134_RS_CONTROL(4),control); + saa_writel(SAA7134_RS_CONTROL(5),control); + } + + /* start DMA */ + saa7134_set_dmabits(dev); + mod_timer(&dev->video_q.timeout, jiffies+BUFFER_TIMEOUT); + return 0; +} + +static int buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct saa7134_fh *fh = q->priv_data; + struct saa7134_dev *dev = fh->dev; + struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + unsigned int size; + int err; + + /* sanity checks */ + if (NULL == fh->fmt) + return -EINVAL; + if (fh->width < 48 || + fh->height < 32 || + fh->width/4 > dev->crop_current.width || + fh->height/4 > dev->crop_current.height || + fh->width > dev->crop_bounds.width || + fh->height > dev->crop_bounds.height) + return -EINVAL; + size = (fh->width * fh->height * fh->fmt->depth) >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < size) + return -EINVAL; + + dprintk("buffer_prepare [%d,size=%dx%d,bytes=%d,fields=%s,%s]\n", + vb->i,fh->width,fh->height,size,v4l2_field_names[field], + fh->fmt->name); + if (buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.size != size || + buf->vb.field != field || + buf->fmt != fh->fmt) { + saa7134_dma_free(q,buf); + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.size = size; + buf->vb.field = field; + buf->fmt = fh->fmt; + buf->pt = &fh->pt_cap; + dev->video_q.curr = NULL; + + err = videobuf_iolock(q,&buf->vb,&dev->ovbuf); + if (err) + goto oops; + err = saa7134_pgtable_build(dev->pci,buf->pt, + dma->sglist, + dma->sglen, + saa7134_buffer_startpage(buf)); + if (err) + goto oops; + } + buf->vb.state = VIDEOBUF_PREPARED; + buf->activate = buffer_activate; + return 0; + + oops: + saa7134_dma_free(q,buf); + return err; +} + +static int +buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + struct saa7134_fh *fh = q->priv_data; + + *size = fh->fmt->depth * fh->width * fh->height >> 3; + if (0 == *count) + *count = gbuffers; + *count = saa7134_buffer_count(*size,*count); + return 0; +} + +static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct saa7134_fh *fh = q->priv_data; + struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + + saa7134_buffer_queue(fh->dev,&fh->dev->video_q,buf); +} + +static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); + + saa7134_dma_free(q,buf); +} + +static struct videobuf_queue_ops video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ */ + +int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c) +{ + const struct v4l2_queryctrl* ctrl; + + ctrl = ctrl_by_id(c->id); + if (NULL == ctrl) + return -EINVAL; + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value = dev->ctl_bright; + break; + case V4L2_CID_HUE: + c->value = dev->ctl_hue; + break; + case V4L2_CID_CONTRAST: + c->value = dev->ctl_contrast; + break; + case V4L2_CID_SATURATION: + c->value = dev->ctl_saturation; + break; + case V4L2_CID_AUDIO_MUTE: + c->value = dev->ctl_mute; + break; + case V4L2_CID_AUDIO_VOLUME: + c->value = dev->ctl_volume; + break; + case V4L2_CID_PRIVATE_INVERT: + c->value = dev->ctl_invert; + break; + case V4L2_CID_HFLIP: + c->value = dev->ctl_mirror; + break; + case V4L2_CID_PRIVATE_Y_EVEN: + c->value = dev->ctl_y_even; + break; + case V4L2_CID_PRIVATE_Y_ODD: + c->value = dev->ctl_y_odd; + break; + case V4L2_CID_PRIVATE_AUTOMUTE: + c->value = dev->ctl_automute; + break; + default: + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL_GPL(saa7134_g_ctrl_internal); + +static int saa7134_g_ctrl(struct file *file, void *priv, struct v4l2_control *c) +{ + struct saa7134_fh *fh = priv; + + return saa7134_g_ctrl_internal(fh->dev, fh, c); +} + +int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c) +{ + const struct v4l2_queryctrl* ctrl; + unsigned long flags; + int restart_overlay = 0; + int err; + + /* When called from the empress code fh == NULL. + That needs to be fixed somehow, but for now this is + good enough. */ + if (fh) { + err = v4l2_prio_check(&dev->prio, fh->prio); + if (0 != err) + return err; + } + err = -EINVAL; + + mutex_lock(&dev->lock); + + ctrl = ctrl_by_id(c->id); + if (NULL == ctrl) + goto error; + + dprintk("set_control name=%s val=%d\n",ctrl->name,c->value); + switch (ctrl->type) { + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER: + if (c->value < ctrl->minimum) + c->value = ctrl->minimum; + if (c->value > ctrl->maximum) + c->value = ctrl->maximum; + break; + default: + /* nothing */; + }; + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + dev->ctl_bright = c->value; + saa_writeb(SAA7134_DEC_LUMA_BRIGHT, dev->ctl_bright); + break; + case V4L2_CID_HUE: + dev->ctl_hue = c->value; + saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); + break; + case V4L2_CID_CONTRAST: + dev->ctl_contrast = c->value; + saa_writeb(SAA7134_DEC_LUMA_CONTRAST, + dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast); + break; + case V4L2_CID_SATURATION: + dev->ctl_saturation = c->value; + saa_writeb(SAA7134_DEC_CHROMA_SATURATION, + dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); + break; + case V4L2_CID_AUDIO_MUTE: + dev->ctl_mute = c->value; + saa7134_tvaudio_setmute(dev); + break; + case V4L2_CID_AUDIO_VOLUME: + dev->ctl_volume = c->value; + saa7134_tvaudio_setvolume(dev,dev->ctl_volume); + break; + case V4L2_CID_PRIVATE_INVERT: + dev->ctl_invert = c->value; + saa_writeb(SAA7134_DEC_LUMA_CONTRAST, + dev->ctl_invert ? -dev->ctl_contrast : dev->ctl_contrast); + saa_writeb(SAA7134_DEC_CHROMA_SATURATION, + dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); + break; + case V4L2_CID_HFLIP: + dev->ctl_mirror = c->value; + restart_overlay = 1; + break; + case V4L2_CID_PRIVATE_Y_EVEN: + dev->ctl_y_even = c->value; + restart_overlay = 1; + break; + case V4L2_CID_PRIVATE_Y_ODD: + dev->ctl_y_odd = c->value; + restart_overlay = 1; + break; + case V4L2_CID_PRIVATE_AUTOMUTE: + { + struct v4l2_priv_tun_config tda9887_cfg; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &dev->tda9887_conf; + + dev->ctl_automute = c->value; + if (dev->tda9887_conf) { + if (dev->ctl_automute) + dev->tda9887_conf |= TDA9887_AUTOMUTE; + else + dev->tda9887_conf &= ~TDA9887_AUTOMUTE; + + saa_call_all(dev, tuner, s_config, &tda9887_cfg); + } + break; + } + default: + goto error; + } + if (restart_overlay && fh && res_check(fh, RESOURCE_OVERLAY)) { + spin_lock_irqsave(&dev->slock,flags); + stop_preview(dev,fh); + start_preview(dev,fh); + spin_unlock_irqrestore(&dev->slock,flags); + } + err = 0; + +error: + mutex_unlock(&dev->lock); + return err; +} +EXPORT_SYMBOL_GPL(saa7134_s_ctrl_internal); + +static int saa7134_s_ctrl(struct file *file, void *f, struct v4l2_control *c) +{ + struct saa7134_fh *fh = f; + + return saa7134_s_ctrl_internal(fh->dev, fh, c); +} + +/* ------------------------------------------------------------------ */ + +static struct videobuf_queue* saa7134_queue(struct saa7134_fh *fh) +{ + struct videobuf_queue* q = NULL; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + q = &fh->cap; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + q = &fh->vbi; + break; + default: + BUG(); + } + return q; +} + +static int saa7134_resource(struct saa7134_fh *fh) +{ + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return RESOURCE_VIDEO; + + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return RESOURCE_VBI; + + BUG(); + return 0; +} + +static int video_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct saa7134_dev *dev = video_drvdata(file); + struct saa7134_fh *fh; + enum v4l2_buf_type type = 0; + int radio = 0; + + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case VFL_TYPE_VBI: + type = V4L2_BUF_TYPE_VBI_CAPTURE; + break; + case VFL_TYPE_RADIO: + radio = 1; + break; + } + + dprintk("open dev=%s radio=%d type=%s\n", video_device_node_name(vdev), + radio, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh),GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + + file->private_data = fh; + fh->dev = dev; + fh->radio = radio; + fh->type = type; + fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24); + fh->width = 720; + fh->height = 576; + v4l2_prio_open(&dev->prio, &fh->prio); + + videobuf_queue_sg_init(&fh->cap, &video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct saa7134_buf), + fh, NULL); + videobuf_queue_sg_init(&fh->vbi, &saa7134_vbi_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, + sizeof(struct saa7134_buf), + fh, NULL); + saa7134_pgtable_alloc(dev->pci,&fh->pt_cap); + saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi); + + if (fh->radio) { + /* switch to radio mode */ + saa7134_tvaudio_setinput(dev,&card(dev).radio); + saa_call_all(dev, tuner, s_radio); + } else { + /* switch to video/vbi mode */ + video_mux(dev,dev->ctl_input); + } + return 0; +} + +static ssize_t +video_read(struct file *file, char __user *data, size_t count, loff_t *ppos) +{ + struct saa7134_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev,RESOURCE_VIDEO)) + return -EBUSY; + return videobuf_read_one(saa7134_queue(fh), + data, count, ppos, + file->f_flags & O_NONBLOCK); + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (!res_get(fh->dev,fh,RESOURCE_VBI)) + return -EBUSY; + return videobuf_read_stream(saa7134_queue(fh), + data, count, ppos, 1, + file->f_flags & O_NONBLOCK); + break; + default: + BUG(); + return 0; + } +} + +static unsigned int +video_poll(struct file *file, struct poll_table_struct *wait) +{ + struct saa7134_fh *fh = file->private_data; + struct videobuf_buffer *buf = NULL; + unsigned int rc = 0; + + if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) + return videobuf_poll_stream(file, &fh->vbi, wait); + + if (res_check(fh,RESOURCE_VIDEO)) { + mutex_lock(&fh->cap.vb_lock); + if (!list_empty(&fh->cap.stream)) + buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream); + } else { + mutex_lock(&fh->cap.vb_lock); + if (UNSET == fh->cap.read_off) { + /* need to capture a new frame */ + if (res_locked(fh->dev,RESOURCE_VIDEO)) + goto err; + if (0 != fh->cap.ops->buf_prepare(&fh->cap,fh->cap.read_buf,fh->cap.field)) + goto err; + fh->cap.ops->buf_queue(&fh->cap,fh->cap.read_buf); + fh->cap.read_off = 0; + } + buf = fh->cap.read_buf; + } + + if (!buf) + goto err; + + poll_wait(file, &buf->done, wait); + if (buf->state == VIDEOBUF_DONE || + buf->state == VIDEOBUF_ERROR) + rc = POLLIN|POLLRDNORM; + mutex_unlock(&fh->cap.vb_lock); + return rc; + +err: + mutex_unlock(&fh->cap.vb_lock); + return POLLERR; +} + +static int video_release(struct file *file) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + struct saa6588_command cmd; + unsigned long flags; + + saa7134_tvaudio_close(dev); + + /* turn off overlay */ + if (res_check(fh, RESOURCE_OVERLAY)) { + spin_lock_irqsave(&dev->slock,flags); + stop_preview(dev,fh); + spin_unlock_irqrestore(&dev->slock,flags); + res_free(dev,fh,RESOURCE_OVERLAY); + } + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO)) { + videobuf_streamoff(&fh->cap); + res_free(dev,fh,RESOURCE_VIDEO); + } + if (fh->cap.read_buf) { + buffer_release(&fh->cap,fh->cap.read_buf); + kfree(fh->cap.read_buf); + } + + /* stop vbi capture */ + if (res_check(fh, RESOURCE_VBI)) { + videobuf_stop(&fh->vbi); + res_free(dev,fh,RESOURCE_VBI); + } + + /* ts-capture will not work in planar mode, so turn it off Hac: 04.05*/ + saa_andorb(SAA7134_OFMT_VIDEO_A, 0x1f, 0); + saa_andorb(SAA7134_OFMT_VIDEO_B, 0x1f, 0); + saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0); + saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0); + + saa_call_all(dev, core, s_power, 0); + if (fh->radio) + saa_call_all(dev, core, ioctl, SAA6588_CMD_CLOSE, &cmd); + + /* free stuff */ + videobuf_mmap_free(&fh->cap); + videobuf_mmap_free(&fh->vbi); + saa7134_pgtable_free(dev->pci,&fh->pt_cap); + saa7134_pgtable_free(dev->pci,&fh->pt_vbi); + + v4l2_prio_close(&dev->prio, fh->prio); + file->private_data = NULL; + kfree(fh); + return 0; +} + +static int video_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct saa7134_fh *fh = file->private_data; + + return videobuf_mmap_mapper(saa7134_queue(fh), vma); +} + +static ssize_t radio_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + struct saa6588_command cmd; + + cmd.block_count = count/3; + cmd.buffer = data; + cmd.instance = file; + cmd.result = -ENODEV; + + saa_call_all(dev, core, ioctl, SAA6588_CMD_READ, &cmd); + + return cmd.result; +} + +static unsigned int radio_poll(struct file *file, poll_table *wait) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + struct saa6588_command cmd; + + cmd.instance = file; + cmd.event_list = wait; + cmd.result = -ENODEV; + saa_call_all(dev, core, ioctl, SAA6588_CMD_POLL, &cmd); + + return cmd.result; +} + +/* ------------------------------------------------------------------ */ + +static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + struct saa7134_tvnorm *norm = dev->tvnorm; + + f->fmt.vbi.sampling_rate = 6750000 * 4; + f->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 64 * 4; + f->fmt.vbi.start[0] = norm->vbi_v_start_0; + f->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 +1; + f->fmt.vbi.start[1] = norm->vbi_v_start_1; + f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; + f->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */ + + return 0; +} + +static int saa7134_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->cap.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; +} + +static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_fh *fh = priv; + + if (saa7134_no_overlay > 0) { + printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + return -EINVAL; + } + f->fmt.win = fh->win; + + return 0; +} + +static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + struct saa7134_format *fmt; + enum v4l2_field field; + unsigned int maxw, maxh; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; + + field = f->fmt.pix.field; + maxw = min(dev->crop_current.width*4, dev->crop_bounds.width); + maxh = min(dev->crop_current.height*4, dev->crop_bounds.height); + + if (V4L2_FIELD_ANY == field) { + field = (f->fmt.pix.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + 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; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + if (saa7134_no_overlay > 0) { + printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + return -EINVAL; + } + + return verify_preview(dev, &f->fmt.win); +} + +static int saa7134_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_fh *fh = priv; + int err; + + err = saa7134_try_fmt_vid_cap(file, priv, f); + if (0 != err) + return err; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->cap.field = f->fmt.pix.field; + return 0; +} + +static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int err; + unsigned long flags; + + if (saa7134_no_overlay > 0) { + printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + return -EINVAL; + } + err = verify_preview(dev, &f->fmt.win); + if (0 != err) + return err; + + mutex_lock(&dev->lock); + + fh->win = f->fmt.win; + fh->nclips = f->fmt.win.clipcount; + + if (fh->nclips > 8) + fh->nclips = 8; + + if (copy_from_user(fh->clips, f->fmt.win.clips, + sizeof(struct v4l2_clip)*fh->nclips)) { + mutex_unlock(&dev->lock); + return -EFAULT; + } + + if (res_check(fh, RESOURCE_OVERLAY)) { + spin_lock_irqsave(&dev->slock, flags); + stop_preview(dev, fh); + start_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + } + + mutex_unlock(&dev->lock); + return 0; +} + +int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c) +{ + const struct v4l2_queryctrl *ctrl; + + if ((c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) && + (c->id < V4L2_CID_PRIVATE_BASE || + c->id >= V4L2_CID_PRIVATE_LASTP1)) + return -EINVAL; + ctrl = ctrl_by_id(c->id); + *c = (NULL != ctrl) ? *ctrl : no_ctrl; + return 0; +} +EXPORT_SYMBOL_GPL(saa7134_queryctrl); + +static int saa7134_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + unsigned int n; + + n = i->index; + if (n >= SAA7134_INPUT_MAX) + return -EINVAL; + if (NULL == card_in(dev, i->index).name) + return -EINVAL; + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(i->name, card_in(dev, n).name); + if (card_in(dev, n).tv) + i->type = V4L2_INPUT_TYPE_TUNER; + i->audioset = 1; + if (n == dev->ctl_input) { + int v1 = saa_readb(SAA7134_STATUS_VIDEO1); + int v2 = saa_readb(SAA7134_STATUS_VIDEO2); + + if (0 != (v1 & 0x40)) + i->status |= V4L2_IN_ST_NO_H_LOCK; + if (0 != (v2 & 0x40)) + i->status |= V4L2_IN_ST_NO_SYNC; + if (0 != (v2 & 0x0e)) + i->status |= V4L2_IN_ST_MACROVISION; + } + i->std = SAA7134_NORMS; + return 0; +} + +static int saa7134_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + *i = dev->ctl_input; + return 0; +} + +static int saa7134_s_input(struct file *file, void *priv, unsigned int i) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int err; + + err = v4l2_prio_check(&dev->prio, fh->prio); + if (0 != err) + return err; + + if (i >= SAA7134_INPUT_MAX) + return -EINVAL; + if (NULL == card_in(dev, i).name) + return -EINVAL; + mutex_lock(&dev->lock); + video_mux(dev, i); + mutex_unlock(&dev->lock); + return 0; +} + +static int saa7134_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + unsigned int tuner_type = dev->tuner_type; + + strcpy(cap->driver, "saa7134"); + strlcpy(cap->card, saa7134_boards[dev->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + V4L2_CAP_TUNER; + if (dev->has_rds) + cap->capabilities |= V4L2_CAP_RDS_CAPTURE; + if (saa7134_no_overlay <= 0) + cap->capabilities |= V4L2_CAP_VIDEO_OVERLAY; + + if ((tuner_type == TUNER_ABSENT) || (tuner_type == UNSET)) + cap->capabilities &= ~V4L2_CAP_TUNER; + return 0; +} + +int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id) +{ + unsigned long flags; + unsigned int i; + v4l2_std_id fixup; + int err; + + /* When called from the empress code fh == NULL. + That needs to be fixed somehow, but for now this is + good enough. */ + if (fh) { + err = v4l2_prio_check(&dev->prio, fh->prio); + if (0 != err) + return err; + } else if (res_locked(dev, RESOURCE_OVERLAY)) { + /* Don't change the std from the mpeg device + if overlay is active. */ + return -EBUSY; + } + + for (i = 0; i < TVNORMS; i++) + if (*id == tvnorms[i].id) + break; + + if (i == TVNORMS) + for (i = 0; i < TVNORMS; i++) + if (*id & tvnorms[i].id) + break; + if (i == TVNORMS) + return -EINVAL; + + if ((*id & V4L2_STD_SECAM) && (secam[0] != '-')) { + if (secam[0] == 'L' || secam[0] == 'l') { + if (secam[1] == 'C' || secam[1] == 'c') + fixup = V4L2_STD_SECAM_LC; + else + fixup = V4L2_STD_SECAM_L; + } else { + if (secam[0] == 'D' || secam[0] == 'd') + fixup = V4L2_STD_SECAM_DK; + else + fixup = V4L2_STD_SECAM; + } + for (i = 0; i < TVNORMS; i++) { + if (fixup == tvnorms[i].id) + break; + } + if (i == TVNORMS) + return -EINVAL; + } + + *id = tvnorms[i].id; + + mutex_lock(&dev->lock); + if (fh && res_check(fh, RESOURCE_OVERLAY)) { + spin_lock_irqsave(&dev->slock, flags); + stop_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + + set_tvnorm(dev, &tvnorms[i]); + + spin_lock_irqsave(&dev->slock, flags); + start_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + } else + set_tvnorm(dev, &tvnorms[i]); + + saa7134_tvaudio_do_scan(dev); + mutex_unlock(&dev->lock); + return 0; +} +EXPORT_SYMBOL_GPL(saa7134_s_std_internal); + +static int saa7134_s_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct saa7134_fh *fh = priv; + + return saa7134_s_std_internal(fh->dev, fh, id); +} + +static int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + *id = dev->tvnorm->id; + return 0; +} + +static int saa7134_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cap) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + cap->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + cap->bounds = dev->crop_bounds; + cap->defrect = dev->crop_defrect; + cap->pixelaspect.numerator = 1; + cap->pixelaspect.denominator = 1; + if (dev->tvnorm->id & V4L2_STD_525_60) { + cap->pixelaspect.numerator = 11; + cap->pixelaspect.denominator = 10; + } + if (dev->tvnorm->id & V4L2_STD_625_50) { + cap->pixelaspect.numerator = 54; + cap->pixelaspect.denominator = 59; + } + return 0; +} + +static int saa7134_g_crop(struct file *file, void *f, struct v4l2_crop *crop) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + crop->c = dev->crop_current; + return 0; +} + +static int saa7134_s_crop(struct file *file, void *f, const struct v4l2_crop *crop) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + struct v4l2_rect *b = &dev->crop_bounds; + struct v4l2_rect *c = &dev->crop_current; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + if (crop->c.height < 0) + return -EINVAL; + if (crop->c.width < 0) + return -EINVAL; + + if (res_locked(fh->dev, RESOURCE_OVERLAY)) + return -EBUSY; + if (res_locked(fh->dev, RESOURCE_VIDEO)) + return -EBUSY; + + *c = crop->c; + if (c->top < b->top) + c->top = b->top; + if (c->top > b->top + b->height) + c->top = b->top + b->height; + if (c->height > b->top - c->top + b->height) + c->height = b->top - c->top + b->height; + + if (c->left < b->left) + c->left = b->left; + if (c->left > b->left + b->width) + c->left = b->left + b->width; + if (c->width > b->left - c->left + b->width) + c->width = b->left - c->left + b->width; + return 0; +} + +static int saa7134_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int n; + + if (0 != t->index) + return -EINVAL; + memset(t, 0, sizeof(*t)); + for (n = 0; n < SAA7134_INPUT_MAX; n++) { + if (card_in(dev, n).tv) + break; + } + if (n == SAA7134_INPUT_MAX) + return -EINVAL; + if (NULL != card_in(dev, n).name) { + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM | + V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2; + t->rangehigh = 0xffffffffUL; + t->rxsubchans = saa7134_tvaudio_getstereo(dev); + t->audmode = saa7134_tvaudio_rx2mode(t->rxsubchans); + } + if (0 != (saa_readb(SAA7134_STATUS_VIDEO1) & 0x03)) + t->signal = 0xffff; + return 0; +} + +static int saa7134_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int rx, mode, err; + + err = v4l2_prio_check(&dev->prio, fh->prio); + if (0 != err) + return err; + + mode = dev->thread.mode; + if (UNSET == mode) { + rx = saa7134_tvaudio_getstereo(dev); + mode = saa7134_tvaudio_rx2mode(rx); + } + if (mode != t->audmode) + dev->thread.mode = t->audmode; + + return 0; +} + +static int saa7134_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + f->frequency = dev->ctl_freq; + + return 0; +} + +static int saa7134_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int err; + + err = v4l2_prio_check(&dev->prio, fh->prio); + if (0 != err) + return err; + + if (0 != f->tuner) + return -EINVAL; + if (0 == fh->radio && V4L2_TUNER_ANALOG_TV != f->type) + return -EINVAL; + if (1 == fh->radio && V4L2_TUNER_RADIO != f->type) + return -EINVAL; + mutex_lock(&dev->lock); + dev->ctl_freq = f->frequency; + + saa_call_all(dev, tuner, s_frequency, f); + + saa7134_tvaudio_do_scan(dev); + mutex_unlock(&dev->lock); + return 0; +} + +static int saa7134_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + strcpy(a->name, "audio"); + return 0; +} + +static int saa7134_s_audio(struct file *file, void *priv, const struct v4l2_audio *a) +{ + return 0; +} + +static int saa7134_g_priority(struct file *file, void *f, enum v4l2_priority *p) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + + *p = v4l2_prio_max(&dev->prio); + return 0; +} + +static int saa7134_s_priority(struct file *file, void *f, + enum v4l2_priority prio) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + + return v4l2_prio_change(&dev->prio, &fh->prio, prio); +} + +static int saa7134_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= FORMATS) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, + sizeof(f->description)); + + f->pixelformat = formats[f->index].fourcc; + + return 0; +} + +static int saa7134_enum_fmt_vid_overlay(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (saa7134_no_overlay > 0) { + printk(KERN_ERR "V4L2_BUF_TYPE_VIDEO_OVERLAY: no_overlay\n"); + return -EINVAL; + } + + if ((f->index >= FORMATS) || formats[f->index].planar) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, + sizeof(f->description)); + + f->pixelformat = formats[f->index].fourcc; + + return 0; +} + +static int saa7134_g_fbuf(struct file *file, void *f, + struct v4l2_framebuffer *fb) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + + *fb = dev->ovbuf; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + + return 0; +} + +static int saa7134_s_fbuf(struct file *file, void *f, + const struct v4l2_framebuffer *fb) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + struct saa7134_format *fmt; + + if (!capable(CAP_SYS_ADMIN) && + !capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* check args */ + fmt = format_by_fourcc(fb->fmt.pixelformat); + if (NULL == fmt) + return -EINVAL; + + /* ok, accept it */ + dev->ovbuf = *fb; + dev->ovfmt = fmt; + if (0 == dev->ovbuf.fmt.bytesperline) + dev->ovbuf.fmt.bytesperline = + dev->ovbuf.fmt.width*fmt->depth/8; + return 0; +} + +static int saa7134_overlay(struct file *file, void *f, unsigned int on) +{ + struct saa7134_fh *fh = f; + struct saa7134_dev *dev = fh->dev; + unsigned long flags; + + if (on) { + if (saa7134_no_overlay > 0) { + dprintk("no_overlay\n"); + return -EINVAL; + } + + if (!res_get(dev, fh, RESOURCE_OVERLAY)) + return -EBUSY; + spin_lock_irqsave(&dev->slock, flags); + start_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + } + if (!on) { + if (!res_check(fh, RESOURCE_OVERLAY)) + return -EINVAL; + spin_lock_irqsave(&dev->slock, flags); + stop_preview(dev, fh); + spin_unlock_irqrestore(&dev->slock, flags); + res_free(dev, fh, RESOURCE_OVERLAY); + } + return 0; +} + +static int saa7134_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct saa7134_fh *fh = priv; + return videobuf_reqbufs(saa7134_queue(fh), p); +} + +static int saa7134_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct saa7134_fh *fh = priv; + return videobuf_querybuf(saa7134_queue(fh), b); +} + +static int saa7134_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct saa7134_fh *fh = priv; + return videobuf_qbuf(saa7134_queue(fh), b); +} + +static int saa7134_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct saa7134_fh *fh = priv; + return videobuf_dqbuf(saa7134_queue(fh), b, + file->f_flags & O_NONBLOCK); +} + +static int saa7134_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int res = saa7134_resource(fh); + + if (!res_get(dev, fh, res)) + return -EBUSY; + + return videobuf_streamon(saa7134_queue(fh)); +} + +static int saa7134_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + int err; + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + int res = saa7134_resource(fh); + + err = videobuf_streamoff(saa7134_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static int saa7134_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *parm) +{ + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register (struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + reg->val = saa_readb(reg->reg); + reg->size = 1; + return 0; +} + +static int vidioc_s_register (struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct saa7134_fh *fh = priv; + struct saa7134_dev *dev = fh->dev; + + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + saa_writeb(reg->reg&0xffffff, reg->val); + return 0; +} +#endif + +static int radio_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + + strcpy(cap->driver, "saa7134"); + strlcpy(cap->card, saa7134_boards[dev->board].name, sizeof(cap->card)); + sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci)); + cap->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + + memset(t, 0, sizeof(*t)); + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + + saa_call_all(dev, tuner, g_tuner, t); + if (dev->input->amux == TV) { + t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11); + t->rxsubchans = (saa_readb(0x529) & 0x08) ? + V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; + } + return 0; +} +static int radio_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + + if (0 != t->index) + return -EINVAL; + + saa_call_all(dev, tuner, s_tuner, t); + return 0; +} + +static int radio_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + + strcpy(i->name, "Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; + + return 0; +} + +static int radio_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int radio_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + memset(a, 0, sizeof(*a)); + strcpy(a->name, "Radio"); + return 0; +} + +static int radio_s_audio(struct file *file, void *priv, + const struct v4l2_audio *a) +{ + return 0; +} + +static int radio_s_input(struct file *filp, void *priv, unsigned int i) +{ + return 0; +} + +static int radio_s_std(struct file *file, void *fh, v4l2_std_id *norm) +{ + return 0; +} + +static int radio_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + const struct v4l2_queryctrl *ctrl; + + if (c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) + return -EINVAL; + if (c->id == V4L2_CID_AUDIO_MUTE) { + ctrl = ctrl_by_id(c->id); + *c = *ctrl; + } else + *c = no_ctrl; + return 0; +} + +static const struct v4l2_file_operations video_fops = +{ + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = saa7134_querycap, + .vidioc_enum_fmt_vid_cap = saa7134_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = saa7134_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = saa7134_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = saa7134_s_fmt_vid_cap, + .vidioc_enum_fmt_vid_overlay = saa7134_enum_fmt_vid_overlay, + .vidioc_g_fmt_vid_overlay = saa7134_g_fmt_vid_overlay, + .vidioc_try_fmt_vid_overlay = saa7134_try_fmt_vid_overlay, + .vidioc_s_fmt_vid_overlay = saa7134_s_fmt_vid_overlay, + .vidioc_g_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap, + .vidioc_g_audio = saa7134_g_audio, + .vidioc_s_audio = saa7134_s_audio, + .vidioc_cropcap = saa7134_cropcap, + .vidioc_reqbufs = saa7134_reqbufs, + .vidioc_querybuf = saa7134_querybuf, + .vidioc_qbuf = saa7134_qbuf, + .vidioc_dqbuf = saa7134_dqbuf, + .vidioc_s_std = saa7134_s_std, + .vidioc_g_std = saa7134_g_std, + .vidioc_enum_input = saa7134_enum_input, + .vidioc_g_input = saa7134_g_input, + .vidioc_s_input = saa7134_s_input, + .vidioc_queryctrl = saa7134_queryctrl, + .vidioc_g_ctrl = saa7134_g_ctrl, + .vidioc_s_ctrl = saa7134_s_ctrl, + .vidioc_streamon = saa7134_streamon, + .vidioc_streamoff = saa7134_streamoff, + .vidioc_g_tuner = saa7134_g_tuner, + .vidioc_s_tuner = saa7134_s_tuner, + .vidioc_g_crop = saa7134_g_crop, + .vidioc_s_crop = saa7134_s_crop, + .vidioc_g_fbuf = saa7134_g_fbuf, + .vidioc_s_fbuf = saa7134_s_fbuf, + .vidioc_overlay = saa7134_overlay, + .vidioc_g_priority = saa7134_g_priority, + .vidioc_s_priority = saa7134_s_priority, + .vidioc_g_parm = saa7134_g_parm, + .vidioc_g_frequency = saa7134_g_frequency, + .vidioc_s_frequency = saa7134_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +static const struct v4l2_file_operations radio_fops = { + .owner = THIS_MODULE, + .open = video_open, + .read = radio_read, + .release = video_release, + .ioctl = video_ioctl2, + .poll = radio_poll, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_s_std = radio_s_std, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_input = radio_g_input, + .vidioc_g_ctrl = saa7134_g_ctrl, + .vidioc_s_ctrl = saa7134_s_ctrl, + .vidioc_g_frequency = saa7134_g_frequency, + .vidioc_s_frequency = saa7134_s_frequency, +}; + +/* ----------------------------------------------------------- */ +/* exported stuff */ + +struct video_device saa7134_video_template = { + .name = "saa7134-video", + .fops = &video_fops, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = SAA7134_NORMS, + .current_norm = V4L2_STD_PAL, +}; + +struct video_device saa7134_radio_template = { + .name = "saa7134-radio", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, +}; + +int saa7134_video_init1(struct saa7134_dev *dev) +{ + /* sanitycheck insmod options */ + if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME) + gbuffers = 2; + if (gbufsize < 0 || gbufsize > gbufsize_max) + gbufsize = gbufsize_max; + gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK; + + /* put some sensible defaults into the data structures ... */ + dev->ctl_bright = ctrl_by_id(V4L2_CID_BRIGHTNESS)->default_value; + dev->ctl_contrast = ctrl_by_id(V4L2_CID_CONTRAST)->default_value; + dev->ctl_hue = ctrl_by_id(V4L2_CID_HUE)->default_value; + dev->ctl_saturation = ctrl_by_id(V4L2_CID_SATURATION)->default_value; + dev->ctl_volume = ctrl_by_id(V4L2_CID_AUDIO_VOLUME)->default_value; + dev->ctl_mute = 1; // ctrl_by_id(V4L2_CID_AUDIO_MUTE)->default_value; + dev->ctl_invert = ctrl_by_id(V4L2_CID_PRIVATE_INVERT)->default_value; + dev->ctl_automute = ctrl_by_id(V4L2_CID_PRIVATE_AUTOMUTE)->default_value; + + if (dev->tda9887_conf && dev->ctl_automute) + dev->tda9887_conf |= TDA9887_AUTOMUTE; + dev->automute = 0; + + INIT_LIST_HEAD(&dev->video_q.queue); + init_timer(&dev->video_q.timeout); + dev->video_q.timeout.function = saa7134_buffer_timeout; + dev->video_q.timeout.data = (unsigned long)(&dev->video_q); + dev->video_q.dev = dev; + + if (saa7134_boards[dev->board].video_out) + saa7134_videoport_init(dev); + + return 0; +} + +int saa7134_videoport_init(struct saa7134_dev *dev) +{ + /* enable video output */ + int vo = saa7134_boards[dev->board].video_out; + int video_reg; + unsigned int vid_port_opts = saa7134_boards[dev->board].vid_port_opts; + + /* Configure videoport */ + saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]); + video_reg = video_out[vo][1]; + if (vid_port_opts & SET_T_CODE_POLARITY_NON_INVERTED) + video_reg &= ~VP_T_CODE_P_INVERTED; + saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_reg); + saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]); + saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]); + video_reg = video_out[vo][5]; + if (vid_port_opts & SET_CLOCK_NOT_DELAYED) + video_reg &= ~VP_CLK_CTRL2_DELAYED; + if (vid_port_opts & SET_CLOCK_INVERTED) + video_reg |= VP_CLK_CTRL1_INVERTED; + saa_writeb(SAA7134_VIDEO_PORT_CTRL5, video_reg); + video_reg = video_out[vo][6]; + if (vid_port_opts & SET_VSYNC_OFF) { + video_reg &= ~VP_VS_TYPE_MASK; + video_reg |= VP_VS_TYPE_OFF; + } + saa_writeb(SAA7134_VIDEO_PORT_CTRL6, video_reg); + saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]); + saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]); + + /* Start videoport */ + saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]); + + return 0; +} + +int saa7134_video_init2(struct saa7134_dev *dev) +{ + /* init video hw */ + set_tvnorm(dev,&tvnorms[0]); + video_mux(dev,0); + saa7134_tvaudio_setmute(dev); + saa7134_tvaudio_setvolume(dev,dev->ctl_volume); + return 0; +} + +void saa7134_irq_video_signalchange(struct saa7134_dev *dev) +{ + static const char *st[] = { + "(no signal)", "NTSC", "PAL", "SECAM" }; + u32 st1,st2; + + st1 = saa_readb(SAA7134_STATUS_VIDEO1); + st2 = saa_readb(SAA7134_STATUS_VIDEO2); + dprintk("DCSDT: pll: %s, sync: %s, norm: %s\n", + (st1 & 0x40) ? "not locked" : "locked", + (st2 & 0x40) ? "no" : "yes", + st[st1 & 0x03]); + dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1); + + if (dev->nosignal) { + /* no video signal -> mute audio */ + if (dev->ctl_automute) + dev->automute = 1; + saa7134_tvaudio_setmute(dev); + } else { + /* wake up tvaudio audio carrier scan thread */ + saa7134_tvaudio_do_scan(dev); + } + + if ((st2 & 0x80) && !noninterlaced && !dev->nosignal) + saa_clearb(SAA7134_SYNC_CTRL, 0x20); + else + saa_setb(SAA7134_SYNC_CTRL, 0x20); + + if (dev->mops && dev->mops->signal_change) + dev->mops->signal_change(dev); +} + + +void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status) +{ + enum v4l2_field field; + + spin_lock(&dev->slock); + if (dev->video_q.curr) { + dev->video_fieldcount++; + field = dev->video_q.curr->vb.field; + if (V4L2_FIELD_HAS_BOTH(field)) { + /* make sure we have seen both fields */ + if ((status & 0x10) == 0x00) { + dev->video_q.curr->top_seen = 1; + goto done; + } + if (!dev->video_q.curr->top_seen) + goto done; + } else if (field == V4L2_FIELD_TOP) { + if ((status & 0x10) != 0x10) + goto done; + } else if (field == V4L2_FIELD_BOTTOM) { + if ((status & 0x10) != 0x00) + goto done; + } + dev->video_q.curr->vb.field_count = dev->video_fieldcount; + saa7134_buffer_finish(dev,&dev->video_q,VIDEOBUF_DONE); + } + saa7134_buffer_next(dev,&dev->video_q); + + done: + spin_unlock(&dev->slock); +} + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h new file mode 100644 index 000000000000..c24b6512bd8f --- /dev/null +++ b/drivers/media/pci/saa7134/saa7134.h @@ -0,0 +1,855 @@ +/* + * + * v4l2 device driver for philips saa7134 based TV cards + * + * (c) 2001,02 Gerd Knorr <kraxel@bytesex.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define SAA7134_VERSION "0, 2, 17" + +#include <linux/pci.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <linux/kdev_t.h> +#include <linux/input.h> +#include <linux/notifier.h> +#include <linux/delay.h> +#include <linux/mutex.h> + +#include <asm/io.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> +#include <media/tuner.h> +#include <media/rc-core.h> +#include <media/ir-kbd-i2c.h> +#include <media/videobuf-dma-sg.h> +#include <sound/core.h> +#include <sound/pcm.h> +#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE) +#include <media/videobuf-dvb.h> +#endif + +#define UNSET (-1U) + +/* ----------------------------------------------------------- */ +/* enums */ + +enum saa7134_tvaudio_mode { + TVAUDIO_FM_MONO = 1, + TVAUDIO_FM_BG_STEREO = 2, + TVAUDIO_FM_SAT_STEREO = 3, + TVAUDIO_FM_K_STEREO = 4, + TVAUDIO_NICAM_AM = 5, + TVAUDIO_NICAM_FM = 6, +}; + +enum saa7134_audio_in { + TV = 1, + LINE1 = 2, + LINE2 = 3, + LINE2_LEFT, +}; + +enum saa7134_video_out { + CCIR656 = 1, +}; + +/* ----------------------------------------------------------- */ +/* static data */ + +struct saa7134_tvnorm { + char *name; + v4l2_std_id id; + + /* video decoder */ + unsigned int sync_control; + unsigned int luma_control; + unsigned int chroma_ctrl1; + unsigned int chroma_gain; + unsigned int chroma_ctrl2; + unsigned int vgate_misc; + + /* video scaler */ + unsigned int h_start; + unsigned int h_stop; + unsigned int video_v_start; + unsigned int video_v_stop; + unsigned int vbi_v_start_0; + unsigned int vbi_v_stop_0; + unsigned int src_timing; + unsigned int vbi_v_start_1; +}; + +struct saa7134_tvaudio { + char *name; + v4l2_std_id std; + enum saa7134_tvaudio_mode mode; + int carr1; + int carr2; +}; + +struct saa7134_format { + char *name; + unsigned int fourcc; + unsigned int depth; + unsigned int pm; + unsigned int vshift; /* vertical downsampling (for planar yuv) */ + unsigned int hshift; /* horizontal downsampling (for planar yuv) */ + unsigned int bswap:1; + unsigned int wswap:1; + unsigned int yuv:1; + unsigned int planar:1; + unsigned int uvswap:1; +}; + +struct saa7134_card_ir { + struct rc_dev *dev; + + char name[32]; + char phys[32]; + unsigned users; + + u32 polling; + u32 last_gpio; + u32 mask_keycode, mask_keydown, mask_keyup; + + bool running; + + struct timer_list timer; + + /* IR core raw decoding */ + u32 raw_decode; +}; + +/* ----------------------------------------------------------- */ +/* card configuration */ + +#define SAA7134_BOARD_NOAUTO UNSET +#define SAA7134_BOARD_UNKNOWN 0 +#define SAA7134_BOARD_PROTEUS_PRO 1 +#define SAA7134_BOARD_FLYVIDEO3000 2 +#define SAA7134_BOARD_FLYVIDEO2000 3 +#define SAA7134_BOARD_EMPRESS 4 +#define SAA7134_BOARD_MONSTERTV 5 +#define SAA7134_BOARD_MD9717 6 +#define SAA7134_BOARD_TVSTATION_RDS 7 +#define SAA7134_BOARD_CINERGY400 8 +#define SAA7134_BOARD_MD5044 9 +#define SAA7134_BOARD_KWORLD 10 +#define SAA7134_BOARD_CINERGY600 11 +#define SAA7134_BOARD_MD7134 12 +#define SAA7134_BOARD_TYPHOON_90031 13 +#define SAA7134_BOARD_ELSA 14 +#define SAA7134_BOARD_ELSA_500TV 15 +#define SAA7134_BOARD_ASUSTeK_TVFM7134 16 +#define SAA7134_BOARD_VA1000POWER 17 +#define SAA7134_BOARD_BMK_MPEX_NOTUNER 18 +#define SAA7134_BOARD_VIDEOMATE_TV 19 +#define SAA7134_BOARD_CRONOS_PLUS 20 +#define SAA7134_BOARD_10MOONSTVMASTER 21 +#define SAA7134_BOARD_MD2819 22 +#define SAA7134_BOARD_BMK_MPEX_TUNER 23 +#define SAA7134_BOARD_TVSTATION_DVR 24 +#define SAA7134_BOARD_ASUSTEK_TVFM7133 25 +#define SAA7134_BOARD_PINNACLE_PCTV_STEREO 26 +#define SAA7134_BOARD_MANLI_MTV002 27 +#define SAA7134_BOARD_MANLI_MTV001 28 +#define SAA7134_BOARD_TG3000TV 29 +#define SAA7134_BOARD_ECS_TVP3XP 30 +#define SAA7134_BOARD_ECS_TVP3XP_4CB5 31 +#define SAA7134_BOARD_AVACSSMARTTV 32 +#define SAA7134_BOARD_AVERMEDIA_DVD_EZMAKER 33 +#define SAA7134_BOARD_NOVAC_PRIMETV7133 34 +#define SAA7134_BOARD_AVERMEDIA_STUDIO_305 35 +#define SAA7134_BOARD_UPMOST_PURPLE_TV 36 +#define SAA7134_BOARD_ITEMS_MTV005 37 +#define SAA7134_BOARD_CINERGY200 38 +#define SAA7134_BOARD_FLYTVPLATINUM_MINI 39 +#define SAA7134_BOARD_VIDEOMATE_TV_PVR 40 +#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUS 41 +#define SAA7134_BOARD_SABRENT_SBTTVFM 42 +#define SAA7134_BOARD_ZOLID_XPERT_TV7134 43 +#define SAA7134_BOARD_EMPIRE_PCI_TV_RADIO_LE 44 +#define SAA7134_BOARD_AVERMEDIA_STUDIO_307 45 +#define SAA7134_BOARD_AVERMEDIA_CARDBUS 46 +#define SAA7134_BOARD_CINERGY400_CARDBUS 47 +#define SAA7134_BOARD_CINERGY600_MK3 48 +#define SAA7134_BOARD_VIDEOMATE_GOLD_PLUS 49 +#define SAA7134_BOARD_PINNACLE_300I_DVBT_PAL 50 +#define SAA7134_BOARD_PROVIDEO_PV952 51 +#define SAA7134_BOARD_AVERMEDIA_305 52 +#define SAA7134_BOARD_ASUSTeK_TVFM7135 53 +#define SAA7134_BOARD_FLYTVPLATINUM_FM 54 +#define SAA7134_BOARD_FLYDVBTDUO 55 +#define SAA7134_BOARD_AVERMEDIA_307 56 +#define SAA7134_BOARD_AVERMEDIA_GO_007_FM 57 +#define SAA7134_BOARD_ADS_INSTANT_TV 58 +#define SAA7134_BOARD_KWORLD_VSTREAM_XPERT 59 +#define SAA7134_BOARD_FLYDVBT_DUO_CARDBUS 60 +#define SAA7134_BOARD_PHILIPS_TOUGH 61 +#define SAA7134_BOARD_VIDEOMATE_TV_GOLD_PLUSII 62 +#define SAA7134_BOARD_KWORLD_XPERT 63 +#define SAA7134_BOARD_FLYTV_DIGIMATRIX 64 +#define SAA7134_BOARD_KWORLD_TERMINATOR 65 +#define SAA7134_BOARD_YUAN_TUN900 66 +#define SAA7134_BOARD_BEHOLD_409FM 67 +#define SAA7134_BOARD_GOTVIEW_7135 68 +#define SAA7134_BOARD_PHILIPS_EUROPA 69 +#define SAA7134_BOARD_VIDEOMATE_DVBT_300 70 +#define SAA7134_BOARD_VIDEOMATE_DVBT_200 71 +#define SAA7134_BOARD_RTD_VFG7350 72 +#define SAA7134_BOARD_RTD_VFG7330 73 +#define SAA7134_BOARD_FLYTVPLATINUM_MINI2 74 +#define SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180 75 +#define SAA7134_BOARD_MONSTERTV_MOBILE 76 +#define SAA7134_BOARD_PINNACLE_PCTV_110i 77 +#define SAA7134_BOARD_ASUSTeK_P7131_DUAL 78 +#define SAA7134_BOARD_SEDNA_PC_TV_CARDBUS 79 +#define SAA7134_BOARD_ASUSTEK_DIGIMATRIX_TV 80 +#define SAA7134_BOARD_PHILIPS_TIGER 81 +#define SAA7134_BOARD_MSI_TVATANYWHERE_PLUS 82 +#define SAA7134_BOARD_CINERGY250PCI 83 +#define SAA7134_BOARD_FLYDVB_TRIO 84 +#define SAA7134_BOARD_AVERMEDIA_777 85 +#define SAA7134_BOARD_FLYDVBT_LR301 86 +#define SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331 87 +#define SAA7134_BOARD_TEVION_DVBT_220RF 88 +#define SAA7134_BOARD_ELSA_700TV 89 +#define SAA7134_BOARD_KWORLD_ATSC110 90 +#define SAA7134_BOARD_AVERMEDIA_A169_B 91 +#define SAA7134_BOARD_AVERMEDIA_A169_B1 92 +#define SAA7134_BOARD_MD7134_BRIDGE_2 93 +#define SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS 94 +#define SAA7134_BOARD_FLYVIDEO3000_NTSC 95 +#define SAA7134_BOARD_MEDION_MD8800_QUADRO 96 +#define SAA7134_BOARD_FLYDVBS_LR300 97 +#define SAA7134_BOARD_PROTEUS_2309 98 +#define SAA7134_BOARD_AVERMEDIA_A16AR 99 +#define SAA7134_BOARD_ASUS_EUROPA2_HYBRID 100 +#define SAA7134_BOARD_PINNACLE_PCTV_310i 101 +#define SAA7134_BOARD_AVERMEDIA_STUDIO_507 102 +#define SAA7134_BOARD_VIDEOMATE_DVBT_200A 103 +#define SAA7134_BOARD_HAUPPAUGE_HVR1110 104 +#define SAA7134_BOARD_CINERGY_HT_PCMCIA 105 +#define SAA7134_BOARD_ENCORE_ENLTV 106 +#define SAA7134_BOARD_ENCORE_ENLTV_FM 107 +#define SAA7134_BOARD_CINERGY_HT_PCI 108 +#define SAA7134_BOARD_PHILIPS_TIGER_S 109 +#define SAA7134_BOARD_AVERMEDIA_M102 110 +#define SAA7134_BOARD_ASUS_P7131_4871 111 +#define SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA 112 +#define SAA7134_BOARD_ECS_TVP3XP_4CB6 113 +#define SAA7134_BOARD_KWORLD_DVBT_210 114 +#define SAA7134_BOARD_SABRENT_TV_PCB05 115 +#define SAA7134_BOARD_10MOONSTVMASTER3 116 +#define SAA7134_BOARD_AVERMEDIA_SUPER_007 117 +#define SAA7134_BOARD_BEHOLD_401 118 +#define SAA7134_BOARD_BEHOLD_403 119 +#define SAA7134_BOARD_BEHOLD_403FM 120 +#define SAA7134_BOARD_BEHOLD_405 121 +#define SAA7134_BOARD_BEHOLD_405FM 122 +#define SAA7134_BOARD_BEHOLD_407 123 +#define SAA7134_BOARD_BEHOLD_407FM 124 +#define SAA7134_BOARD_BEHOLD_409 125 +#define SAA7134_BOARD_BEHOLD_505FM 126 +#define SAA7134_BOARD_BEHOLD_507_9FM 127 +#define SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM 128 +#define SAA7134_BOARD_BEHOLD_607FM_MK3 129 +#define SAA7134_BOARD_BEHOLD_M6 130 +#define SAA7134_BOARD_TWINHAN_DTV_DVB_3056 131 +#define SAA7134_BOARD_GENIUS_TVGO_A11MCE 132 +#define SAA7134_BOARD_PHILIPS_SNAKE 133 +#define SAA7134_BOARD_CREATIX_CTX953 134 +#define SAA7134_BOARD_MSI_TVANYWHERE_AD11 135 +#define SAA7134_BOARD_AVERMEDIA_CARDBUS_506 136 +#define SAA7134_BOARD_AVERMEDIA_A16D 137 +#define SAA7134_BOARD_AVERMEDIA_M115 138 +#define SAA7134_BOARD_VIDEOMATE_T750 139 +#define SAA7134_BOARD_AVERMEDIA_A700_PRO 140 +#define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141 +#define SAA7134_BOARD_BEHOLD_H6 142 +#define SAA7134_BOARD_BEHOLD_M63 143 +#define SAA7134_BOARD_BEHOLD_M6_EXTRA 144 +#define SAA7134_BOARD_AVERMEDIA_M103 145 +#define SAA7134_BOARD_ASUSTeK_P7131_ANALOG 146 +#define SAA7134_BOARD_ASUSTeK_TIGER_3IN1 147 +#define SAA7134_BOARD_ENCORE_ENLTV_FM53 148 +#define SAA7134_BOARD_AVERMEDIA_M135A 149 +#define SAA7134_BOARD_REAL_ANGEL_220 150 +#define SAA7134_BOARD_ADS_INSTANT_HDTV_PCI 151 +#define SAA7134_BOARD_ASUSTeK_TIGER 152 +#define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 +#define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154 +#define SAA7134_BOARD_HAUPPAUGE_HVR1150 155 +#define SAA7134_BOARD_HAUPPAUGE_HVR1120 156 +#define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157 +#define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158 +#define SAA7134_BOARD_BEHOLD_505RDS_MK5 159 +#define SAA7134_BOARD_BEHOLD_507RDS_MK3 160 +#define SAA7134_BOARD_BEHOLD_507RDS_MK5 161 +#define SAA7134_BOARD_BEHOLD_607FM_MK5 162 +#define SAA7134_BOARD_BEHOLD_609FM_MK3 163 +#define SAA7134_BOARD_BEHOLD_609FM_MK5 164 +#define SAA7134_BOARD_BEHOLD_607RDS_MK3 165 +#define SAA7134_BOARD_BEHOLD_607RDS_MK5 166 +#define SAA7134_BOARD_BEHOLD_609RDS_MK3 167 +#define SAA7134_BOARD_BEHOLD_609RDS_MK5 168 +#define SAA7134_BOARD_VIDEOMATE_S350 169 +#define SAA7134_BOARD_AVERMEDIA_STUDIO_505 170 +#define SAA7134_BOARD_BEHOLD_X7 171 +#define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172 +#define SAA7134_BOARD_ZOLID_HYBRID_PCI 173 +#define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174 +#define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175 +#define SAA7134_BOARD_BEHOLD_505RDS_MK3 176 +#define SAA7134_BOARD_HAWELL_HW_404M7 177 +#define SAA7134_BOARD_BEHOLD_H7 178 +#define SAA7134_BOARD_BEHOLD_A7 179 +#define SAA7134_BOARD_AVERMEDIA_M733A 180 +#define SAA7134_BOARD_TECHNOTREND_BUDGET_T3000 181 +#define SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG 182 +#define SAA7134_BOARD_VIDEOMATE_M1F 183 +#define SAA7134_BOARD_ENCORE_ENLTV_FM3 184 +#define SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2 185 +#define SAA7134_BOARD_BEHOLD_501 186 +#define SAA7134_BOARD_BEHOLD_503FM 187 +#define SAA7134_BOARD_SENSORAY811_911 188 +#define SAA7134_BOARD_KWORLD_PC150U 189 +#define SAA7134_BOARD_ASUSTeK_PS3_100 190 + +#define SAA7134_MAXBOARDS 32 +#define SAA7134_INPUT_MAX 8 + +/* ----------------------------------------------------------- */ +/* Since we support 2 remote types, lets tell them apart */ + +#define SAA7134_REMOTE_GPIO 1 +#define SAA7134_REMOTE_I2C 2 + +/* ----------------------------------------------------------- */ +/* Video Output Port Register Initialization Options */ + +#define SET_T_CODE_POLARITY_NON_INVERTED (1 << 0) +#define SET_CLOCK_NOT_DELAYED (1 << 1) +#define SET_CLOCK_INVERTED (1 << 2) +#define SET_VSYNC_OFF (1 << 3) + +struct saa7134_input { + char *name; + unsigned int vmux; + enum saa7134_audio_in amux; + unsigned int gpio; + unsigned int tv:1; +}; + +enum saa7134_mpeg_type { + SAA7134_MPEG_UNUSED, + SAA7134_MPEG_EMPRESS, + SAA7134_MPEG_DVB, +}; + +enum saa7134_mpeg_ts_type { + SAA7134_MPEG_TS_PARALLEL = 0, + SAA7134_MPEG_TS_SERIAL, +}; + +struct saa7134_board { + char *name; + unsigned int audio_clock; + + /* input switching */ + unsigned int gpiomask; + struct saa7134_input inputs[SAA7134_INPUT_MAX]; + struct saa7134_input radio; + struct saa7134_input mute; + + /* i2c chip info */ + unsigned int tuner_type; + unsigned int radio_type; + unsigned char tuner_addr; + unsigned char radio_addr; + unsigned char empress_addr; + unsigned char rds_addr; + + unsigned int tda9887_conf; + unsigned int tuner_config; + + /* peripheral I/O */ + enum saa7134_video_out video_out; + enum saa7134_mpeg_type mpeg; + enum saa7134_mpeg_ts_type ts_type; + unsigned int vid_port_opts; + unsigned int ts_force_val:1; +}; + +#define card_has_radio(dev) (NULL != saa7134_boards[dev->board].radio.name) +#define card_is_empress(dev) (SAA7134_MPEG_EMPRESS == saa7134_boards[dev->board].mpeg) +#define card_is_dvb(dev) (SAA7134_MPEG_DVB == saa7134_boards[dev->board].mpeg) +#define card_has_mpeg(dev) (SAA7134_MPEG_UNUSED != saa7134_boards[dev->board].mpeg) +#define card(dev) (saa7134_boards[dev->board]) +#define card_in(dev,n) (saa7134_boards[dev->board].inputs[n]) + +/* ----------------------------------------------------------- */ +/* device / file handle status */ + +#define RESOURCE_OVERLAY 1 +#define RESOURCE_VIDEO 2 +#define RESOURCE_VBI 4 + +#define INTERLACE_AUTO 0 +#define INTERLACE_ON 1 +#define INTERLACE_OFF 2 + +#define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ +#define TS_BUFFER_TIMEOUT msecs_to_jiffies(1000) /* 1 second */ + +struct saa7134_dev; +struct saa7134_dma; + +/* saa7134 page table */ +struct saa7134_pgtable { + unsigned int size; + __le32 *cpu; + dma_addr_t dma; +}; + +/* tvaudio thread status */ +struct saa7134_thread { + struct task_struct *thread; + unsigned int scan1; + unsigned int scan2; + unsigned int mode; + unsigned int stopped; +}; + +/* buffer for one video/vbi/ts frame */ +struct saa7134_buf { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + /* saa7134 specific */ + struct saa7134_format *fmt; + unsigned int top_seen; + int (*activate)(struct saa7134_dev *dev, + struct saa7134_buf *buf, + struct saa7134_buf *next); + + /* page tables */ + struct saa7134_pgtable *pt; +}; + +struct saa7134_dmaqueue { + struct saa7134_dev *dev; + struct saa7134_buf *curr; + struct list_head queue; + struct timer_list timeout; + unsigned int need_two; +}; + +/* video filehandle status */ +struct saa7134_fh { + struct saa7134_dev *dev; + unsigned int radio; + enum v4l2_buf_type type; + unsigned int resources; + enum v4l2_priority prio; + + /* video overlay */ + struct v4l2_window win; + struct v4l2_clip clips[8]; + unsigned int nclips; + + /* video capture */ + struct saa7134_format *fmt; + unsigned int width,height; + struct videobuf_queue cap; + struct saa7134_pgtable pt_cap; + + /* vbi capture */ + struct videobuf_queue vbi; + struct saa7134_pgtable pt_vbi; +}; + +/* dmasound dsp status */ +struct saa7134_dmasound { + struct mutex lock; + int minor_mixer; + int minor_dsp; + unsigned int users_dsp; + + /* mixer */ + enum saa7134_audio_in input; + unsigned int count; + unsigned int line1; + unsigned int line2; + + /* dsp */ + unsigned int afmt; + unsigned int rate; + unsigned int channels; + unsigned int recording_on; + unsigned int dma_running; + unsigned int blocks; + unsigned int blksize; + unsigned int bufsize; + struct saa7134_pgtable pt; + struct videobuf_dmabuf dma; + unsigned int dma_blk; + unsigned int read_offset; + unsigned int read_count; + void * priv_data; + struct snd_pcm_substream *substream; +}; + +/* ts/mpeg status */ +struct saa7134_ts { + /* TS capture */ + struct saa7134_pgtable pt_ts; + int nr_packets; + int nr_bufs; +}; + +/* ts/mpeg ops */ +struct saa7134_mpeg_ops { + enum saa7134_mpeg_type type; + struct list_head next; + int (*init)(struct saa7134_dev *dev); + int (*fini)(struct saa7134_dev *dev); + void (*signal_change)(struct saa7134_dev *dev); +}; + +/* global device status */ +struct saa7134_dev { + struct list_head devlist; + struct mutex lock; + spinlock_t slock; + struct v4l2_prio_state prio; + struct v4l2_device v4l2_dev; + /* workstruct for loading modules */ + struct work_struct request_module_wk; + + /* insmod option/autodetected */ + int autodetected; + + /* various device info */ + unsigned int resources; + struct video_device *video_dev; + struct video_device *radio_dev; + struct video_device *vbi_dev; + struct saa7134_dmasound dmasound; + + /* infrared remote */ + int has_remote; + struct saa7134_card_ir *remote; + + /* pci i/o */ + char name[32]; + int nr; + struct pci_dev *pci; + unsigned char pci_rev,pci_lat; + __u32 __iomem *lmmio; + __u8 __iomem *bmmio; + + /* config info */ + unsigned int board; + unsigned int tuner_type; + unsigned int radio_type; + unsigned char tuner_addr; + unsigned char radio_addr; + + unsigned int tda9887_conf; + unsigned int gpio_value; + + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_client i2c_client; + unsigned char eedata[256]; + int has_rds; + + /* video overlay */ + struct v4l2_framebuffer ovbuf; + struct saa7134_format *ovfmt; + unsigned int ovenable; + enum v4l2_field ovfield; + + /* video+ts+vbi capture */ + struct saa7134_dmaqueue video_q; + struct saa7134_dmaqueue vbi_q; + unsigned int video_fieldcount; + unsigned int vbi_fieldcount; + + /* various v4l controls */ + struct saa7134_tvnorm *tvnorm; /* video */ + struct saa7134_tvaudio *tvaudio; + unsigned int ctl_input; + int ctl_bright; + int ctl_contrast; + int ctl_hue; + int ctl_saturation; + int ctl_freq; + int ctl_mute; /* audio */ + int ctl_volume; + int ctl_invert; /* private */ + int ctl_mirror; + int ctl_y_odd; + int ctl_y_even; + int ctl_automute; + + /* crop */ + struct v4l2_rect crop_bounds; + struct v4l2_rect crop_defrect; + struct v4l2_rect crop_current; + + /* other global state info */ + unsigned int automute; + struct saa7134_thread thread; + struct saa7134_input *input; + struct saa7134_input *hw_input; + unsigned int hw_mute; + int last_carrier; + int nosignal; + unsigned int insuspend; + + /* I2C keyboard data */ + struct IR_i2c_init_data init_data; + + /* SAA7134_MPEG_* */ + struct saa7134_ts ts; + struct saa7134_dmaqueue ts_q; + int ts_started; + struct saa7134_mpeg_ops *mops; + + /* SAA7134_MPEG_EMPRESS only */ + struct video_device *empress_dev; + struct videobuf_queue empress_tsq; + atomic_t empress_users; + struct work_struct empress_workqueue; + int empress_started; + +#if defined(CONFIG_VIDEO_SAA7134_DVB) || defined(CONFIG_VIDEO_SAA7134_DVB_MODULE) + /* SAA7134_MPEG_DVB only */ + struct videobuf_dvb_frontends frontends; + int (*original_demod_sleep)(struct dvb_frontend *fe); + int (*original_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); + int (*original_set_high_voltage)(struct dvb_frontend *fe, long arg); +#endif + void (*gate_ctrl)(struct saa7134_dev *dev, int open); +}; + +/* ----------------------------------------------------------- */ + +#define saa_readl(reg) readl(dev->lmmio + (reg)) +#define saa_writel(reg,value) writel((value), dev->lmmio + (reg)); +#define saa_andorl(reg,mask,value) \ + writel((readl(dev->lmmio+(reg)) & ~(mask)) |\ + ((value) & (mask)), dev->lmmio+(reg)) +#define saa_setl(reg,bit) saa_andorl((reg),(bit),(bit)) +#define saa_clearl(reg,bit) saa_andorl((reg),(bit),0) + +#define saa_readb(reg) readb(dev->bmmio + (reg)) +#define saa_writeb(reg,value) writeb((value), dev->bmmio + (reg)); +#define saa_andorb(reg,mask,value) \ + writeb((readb(dev->bmmio+(reg)) & ~(mask)) |\ + ((value) & (mask)), dev->bmmio+(reg)) +#define saa_setb(reg,bit) saa_andorb((reg),(bit),(bit)) +#define saa_clearb(reg,bit) saa_andorb((reg),(bit),0) + +#define saa_wait(us) { udelay(us); } + +#define SAA7134_NORMS (\ + V4L2_STD_PAL | V4L2_STD_PAL_N | \ + V4L2_STD_PAL_Nc | V4L2_STD_SECAM | \ + V4L2_STD_NTSC | V4L2_STD_PAL_M | \ + V4L2_STD_PAL_60) + +#define GRP_EMPRESS (1) +#define saa_call_all(dev, o, f, args...) do { \ + if (dev->gate_ctrl) \ + dev->gate_ctrl(dev, 1); \ + v4l2_device_call_all(&(dev)->v4l2_dev, 0, o, f , ##args); \ + if (dev->gate_ctrl) \ + dev->gate_ctrl(dev, 0); \ +} while (0) + +#define saa_call_empress(dev, o, f, args...) ({ \ + long _rc; \ + if (dev->gate_ctrl) \ + dev->gate_ctrl(dev, 1); \ + _rc = v4l2_device_call_until_err(&(dev)->v4l2_dev, \ + GRP_EMPRESS, o, f , ##args); \ + if (dev->gate_ctrl) \ + dev->gate_ctrl(dev, 0); \ + _rc; \ +}) + +/* ----------------------------------------------------------- */ +/* saa7134-core.c */ + +extern struct list_head saa7134_devlist; +extern struct mutex saa7134_devlist_lock; +extern int saa7134_no_overlay; + +void saa7134_track_gpio(struct saa7134_dev *dev, char *msg); +void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value); + +#define SAA7134_PGTABLE_SIZE 4096 + +int saa7134_pgtable_alloc(struct pci_dev *pci, struct saa7134_pgtable *pt); +int saa7134_pgtable_build(struct pci_dev *pci, struct saa7134_pgtable *pt, + struct scatterlist *list, unsigned int length, + unsigned int startpage); +void saa7134_pgtable_free(struct pci_dev *pci, struct saa7134_pgtable *pt); + +int saa7134_buffer_count(unsigned int size, unsigned int count); +int saa7134_buffer_startpage(struct saa7134_buf *buf); +unsigned long saa7134_buffer_base(struct saa7134_buf *buf); + +int saa7134_buffer_queue(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, + struct saa7134_buf *buf); +void saa7134_buffer_finish(struct saa7134_dev *dev, struct saa7134_dmaqueue *q, + unsigned int state); +void saa7134_buffer_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q); +void saa7134_buffer_timeout(unsigned long data); +void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf); + +int saa7134_set_dmabits(struct saa7134_dev *dev); + +extern int (*saa7134_dmasound_init)(struct saa7134_dev *dev); +extern int (*saa7134_dmasound_exit)(struct saa7134_dev *dev); + + +/* ----------------------------------------------------------- */ +/* saa7134-cards.c */ + +extern struct saa7134_board saa7134_boards[]; +extern const unsigned int saa7134_bcount; +extern struct pci_device_id __devinitdata saa7134_pci_tbl[]; + +extern int saa7134_board_init1(struct saa7134_dev *dev); +extern int saa7134_board_init2(struct saa7134_dev *dev); +int saa7134_tuner_callback(void *priv, int component, int command, int arg); + + +/* ----------------------------------------------------------- */ +/* saa7134-i2c.c */ + +int saa7134_i2c_register(struct saa7134_dev *dev); +int saa7134_i2c_unregister(struct saa7134_dev *dev); + + +/* ----------------------------------------------------------- */ +/* saa7134-video.c */ + +extern unsigned int video_debug; +extern struct video_device saa7134_video_template; +extern struct video_device saa7134_radio_template; + +int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c); +int saa7134_g_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, struct v4l2_control *c); +int saa7134_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c); +int saa7134_s_std_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, v4l2_std_id *id); + +int saa7134_videoport_init(struct saa7134_dev *dev); +void saa7134_set_tvnorm_hw(struct saa7134_dev *dev); + +int saa7134_video_init1(struct saa7134_dev *dev); +int saa7134_video_init2(struct saa7134_dev *dev); +void saa7134_irq_video_signalchange(struct saa7134_dev *dev); +void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status); + + +/* ----------------------------------------------------------- */ +/* saa7134-ts.c */ + +#define TS_PACKET_SIZE 188 /* TS packets 188 bytes */ + +extern struct videobuf_queue_ops saa7134_ts_qops; + +int saa7134_ts_init1(struct saa7134_dev *dev); +int saa7134_ts_fini(struct saa7134_dev *dev); +void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status); + +int saa7134_ts_register(struct saa7134_mpeg_ops *ops); +void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops); + +int saa7134_ts_init_hw(struct saa7134_dev *dev); + +int saa7134_ts_start(struct saa7134_dev *dev); +int saa7134_ts_stop(struct saa7134_dev *dev); + +/* ----------------------------------------------------------- */ +/* saa7134-vbi.c */ + +extern struct videobuf_queue_ops saa7134_vbi_qops; +extern struct video_device saa7134_vbi_template; + +int saa7134_vbi_init1(struct saa7134_dev *dev); +int saa7134_vbi_fini(struct saa7134_dev *dev); +void saa7134_irq_vbi_done(struct saa7134_dev *dev, unsigned long status); + + +/* ----------------------------------------------------------- */ +/* saa7134-tvaudio.c */ + +int saa7134_tvaudio_rx2mode(u32 rx); + +void saa7134_tvaudio_setmute(struct saa7134_dev *dev); +void saa7134_tvaudio_setinput(struct saa7134_dev *dev, + struct saa7134_input *in); +void saa7134_tvaudio_setvolume(struct saa7134_dev *dev, int level); +int saa7134_tvaudio_getstereo(struct saa7134_dev *dev); + +void saa7134_tvaudio_init(struct saa7134_dev *dev); +int saa7134_tvaudio_init2(struct saa7134_dev *dev); +int saa7134_tvaudio_fini(struct saa7134_dev *dev); +int saa7134_tvaudio_do_scan(struct saa7134_dev *dev); +int saa7134_tvaudio_close(struct saa7134_dev *dev); + +int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value); + +void saa7134_enable_i2s(struct saa7134_dev *dev); + +/* ----------------------------------------------------------- */ +/* saa7134-oss.c */ + +extern const struct file_operations saa7134_dsp_fops; +extern const struct file_operations saa7134_mixer_fops; + +int saa7134_oss_init1(struct saa7134_dev *dev); +int saa7134_oss_fini(struct saa7134_dev *dev); +void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status); + +/* ----------------------------------------------------------- */ +/* saa7134-input.c */ + +#if defined(CONFIG_VIDEO_SAA7134_RC) +int saa7134_input_init1(struct saa7134_dev *dev); +void saa7134_input_fini(struct saa7134_dev *dev); +void saa7134_input_irq(struct saa7134_dev *dev); +void saa7134_probe_i2c_ir(struct saa7134_dev *dev); +int saa7134_ir_start(struct saa7134_dev *dev); +void saa7134_ir_stop(struct saa7134_dev *dev); +#else +#define saa7134_input_init1(dev) ((void)0) +#define saa7134_input_fini(dev) ((void)0) +#define saa7134_input_irq(dev) ((void)0) +#define saa7134_probe_i2c_ir(dev) ((void)0) +#define saa7134_ir_start(dev) ((void)0) +#define saa7134_ir_stop(dev) ((void)0) +#endif |